mirror of
https://github.com/golang/go.git
synced 2026-01-29 23:22:06 +03:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f6557233c | ||
|
|
4de7b10483 | ||
|
|
3b6f4b04ba | ||
|
|
740dfbadbd | ||
|
|
edecc650ec | ||
|
|
2e150a0aa7 | ||
|
|
f0377a2851 | ||
|
|
2287d95e9b | ||
|
|
a2f37b7fe1 | ||
|
|
ca0b97e80a | ||
|
|
230c3918a8 | ||
|
|
b67902fee7 | ||
|
|
5a589904a3 | ||
|
|
f75aafdf56 | ||
|
|
b261730e56 | ||
|
|
40712a9625 | ||
|
|
24f46bd34f | ||
|
|
c24b5d43a6 | ||
|
|
dc3612e0d1 | ||
|
|
6f12826a86 | ||
|
|
3129c67db7 | ||
|
|
bb8706890b | ||
|
|
287be1e42d | ||
|
|
308bdd0256 | ||
|
|
9b4e323e88 | ||
|
|
a5add8c726 | ||
|
|
e464c08bef | ||
|
|
c0f5de1ad2 | ||
|
|
1861ac159e | ||
|
|
759c2c48ea | ||
|
|
09fa0b8e95 | ||
|
|
0d81858868 | ||
|
|
681a667ced | ||
|
|
806a84d3f1 | ||
|
|
87d4bd1997 | ||
|
|
1e933ed7c0 | ||
|
|
78d024a588 | ||
|
|
09fc3cc5df | ||
|
|
17a48e0a00 |
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.7">Go 1.7</a> <small>(August 2016)</small></li>
|
||||
<li><a href="/doc/go1.6">Go 1.6</a> <small>(February 2016)</small></li>
|
||||
<li><a href="/doc/go1.5">Go 1.5</a> <small>(August 2015)</small></li>
|
||||
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||
|
||||
@@ -30,6 +30,39 @@ to fix critical security problems in both Go 1.4 and Go 1.5 as they arise.
|
||||
See the <a href="/security">security policy</a> for more details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.7">go1.7 (released 2016/08/15)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.7 is a major release of Go.
|
||||
Read the <a href="/doc/go1.7">Go 1.7 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.7.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.7.1 (released 2016/09/07) includes fixes to the compiler, runtime,
|
||||
documentation, and the <code>compress/flate</code>, <code>hash/crc32</code>,
|
||||
<code>io</code>, <code>net</code>, <code>net/http</code>,
|
||||
<code>path/filepath</code>, <code>reflect</code>, and <code>syscall</code>
|
||||
packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.1">Go
|
||||
1.7.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.2 should not be used. It was tagged but not fully released.
|
||||
The release was deferred due to a last minute bug report.
|
||||
Use go1.7.3 instead, and refer to the summary of changes below.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.3 (released 2016/10/19) includes fixes to the compiler, runtime,
|
||||
and the <code>crypto/cipher</code>, <code>crypto/tls</code>,
|
||||
<code>net/http</code>, and <code>strings</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.3">Go
|
||||
1.7.3 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--{
|
||||
"Title": "Go 1.7 Release Notes DRAFT",
|
||||
"Title": "Go 1.7 Release Notes",
|
||||
"Path": "/doc/go1.7",
|
||||
"Template": true
|
||||
}-->
|
||||
@@ -25,15 +25,6 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<p>
|
||||
<!-- TODO: REMOVE THIS COMMENT -->
|
||||
<!-- TODO: Also remove "DRAFT" in the "Title" at the top of this file. -->
|
||||
<i>NOTE: This is a DRAFT of the Go 1.7 release notes, prepared for the Go 1.7 beta.
|
||||
Go 1.7 has NOT yet been released.
|
||||
By our regular schedule, it is expected some time in August 2016.
|
||||
</i>
|
||||
</p>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.7</h2>
|
||||
|
||||
<p>
|
||||
@@ -919,7 +910,7 @@ For example, the address on which a request received is
|
||||
<p>
|
||||
The server's <a href="/pkg/net/http/#Server.Serve"><code>Serve</code></a> method
|
||||
now only enables HTTP/2 support if the <code>Server.TLSConfig</code> field is <code>nil</code>
|
||||
or includes <code>"h2"</code> in its <code>TLSConfig.NextProto</code>.
|
||||
or includes <code>"h2"</code> in its <code>TLSConfig.NextProtos</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
238
doc/gopher/favicon.svg
Normal file
238
doc/gopher/favicon.svg
Normal file
@@ -0,0 +1,238 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32.000001"
|
||||
id="svg4416"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:export-filename="../../favicon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4418" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="17.966652"
|
||||
inkscape:cy="9.2991824"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
showguides="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5148" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4421">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="icon"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1020.3622)">
|
||||
<ellipse
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="ellipse4216"
|
||||
cx="-907.35657"
|
||||
cy="479.90009"
|
||||
rx="3.5793996"
|
||||
ry="3.8207953"
|
||||
transform="matrix(-0.49169095,-0.87076978,-0.87076978,0.49169095,0,0)"
|
||||
inkscape:transform-center-x="0.67794294"
|
||||
inkscape:transform-center-y="-2.3634048" />
|
||||
<ellipse
|
||||
inkscape:transform-center-y="-2.3633882"
|
||||
inkscape:transform-center-x="-0.67793718"
|
||||
transform="matrix(0.49169095,-0.87076978,0.87076978,0.49169095,0,0)"
|
||||
ry="3.8207953"
|
||||
rx="3.5793996"
|
||||
cy="507.8461"
|
||||
cx="-891.57654"
|
||||
id="ellipse4463"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 16.091693,1021.3642 c -1.105749,0.01 -2.210341,0.049 -3.31609,0.09 C 6.8422558,1021.6738 2,1026.3942 2,1032.3622 c 0,2.9786 0,13 0,20 l 28,0 c 0,-8 0,-16 0,-20 0,-5.9683 -4.667345,-10.4912 -10.59023,-10.908 -1.10575,-0.078 -2.212328,-0.099 -3.318077,-0.09 z"
|
||||
id="path4465"
|
||||
sodipodi:nodetypes="ccsccscc" />
|
||||
<path
|
||||
inkscape:transform-center-y="-1.3604657"
|
||||
inkscape:transform-center-x="-0.98424303"
|
||||
sodipodi:nodetypes="sssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4469"
|
||||
d="m 4.6078867,1025.0462 c 0.459564,0.2595 1.818262,1.2013 1.980983,1.648 0.183401,0.5035 0.159385,1.0657 -0.114614,1.551 -0.346627,0.6138 -1.005341,0.9487 -1.696421,0.9365 -0.339886,-0.01 -1.720283,-0.6372 -2.042561,-0.8192 -0.97754,-0.5519 -1.350795,-1.7418 -0.833686,-2.6576 0.517109,-0.9158 1.728749,-1.2107 2.706299,-0.6587 z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32850246;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4473"
|
||||
width="3.0866659"
|
||||
height="3.5313663"
|
||||
x="14.406213"
|
||||
y="1035.6842"
|
||||
ry="0.62426329" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 16,1023.3622 c -9,0 -12,3.7153 -12,9 l 0,20 24,0 c -0.04889,-7.3562 0,-18 0,-20 0,-5.2848 -3,-9 -12,-9 z"
|
||||
id="path4471"
|
||||
sodipodi:nodetypes="zsccsz" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 27.074073,1025.0462 c -0.45957,0.2595 -1.818257,1.2013 -1.980979,1.648 -0.183401,0.5035 -0.159384,1.0657 0.114614,1.551 0.346627,0.6138 1.005335,0.9487 1.696415,0.9365 0.33988,-0.01 1.72029,-0.6372 2.04256,-0.8192 0.97754,-0.5519 1.35079,-1.7418 0.83369,-2.6576 -0.51711,-0.9158 -1.72876,-1.2107 -2.7063,-0.6587 z"
|
||||
id="path4481"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssss"
|
||||
inkscape:transform-center-x="0.98424094"
|
||||
inkscape:transform-center-y="-1.3604657" />
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle4477"
|
||||
cx="21.175734"
|
||||
cy="1030.3542"
|
||||
r="4.6537542"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
r="4.8316345"
|
||||
cy="1030.3542"
|
||||
cx="10.339486"
|
||||
id="circle4483"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<rect
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32941176;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4246"
|
||||
width="3.6673687"
|
||||
height="4.1063409"
|
||||
x="14.115863"
|
||||
y="1035.9174"
|
||||
ry="0.72590536" />
|
||||
<rect
|
||||
ry="0.72590536"
|
||||
y="1035.2253"
|
||||
x="14.115863"
|
||||
height="4.1063409"
|
||||
width="3.6673687"
|
||||
id="rect4485"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#fffcfb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32941176;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 19.999735,1036.5289 c 0,0.838 -0.871228,1.2682 -2.144766,1.1659 -0.02366,0 -0.04795,-0.6004 -0.254147,-0.5832 -0.503669,0.042 -1.095902,-0.02 -1.685964,-0.02 -0.612939,0 -1.206342,0.1826 -1.68549,0.017 -0.110233,-0.038 -0.178298,0.5838 -0.261532,0.5816 -1.243685,-0.033 -2.078803,-0.3383 -2.078803,-1.1618 0,-1.2118 1.815635,-2.1941 4.055351,-2.1941 2.239704,0 4.055351,0.9823 4.055351,2.1941 z"
|
||||
id="path4487"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4489"
|
||||
d="m 19.977414,1035.7004 c 0,0.5685 -0.433659,0.8554 -1.138091,1.0001 -0.291933,0.06 -0.630371,0.096 -1.003719,0.1166 -0.56405,0.032 -1.207782,0.031 -1.89122,0.031 -0.672834,0 -1.307182,0 -1.864904,-0.029 -0.306268,-0.017 -0.589429,-0.043 -0.843164,-0.084 -0.813833,-0.1318 -1.324962,-0.417 -1.324962,-1.0344 0,-1.1601 1.805642,-2.1006 4.03303,-2.1006 2.227377,0 4.03303,0.9405 4.03303,2.1006 z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c38c74;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<ellipse
|
||||
cy="1033.8501"
|
||||
cx="15.944382"
|
||||
id="ellipse4491"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#23201f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
rx="2.0801733"
|
||||
ry="1.343747"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#171311;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle4493"
|
||||
cx="12.414201"
|
||||
cy="1030.3542"
|
||||
r="1.9630634"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
r="1.9630634"
|
||||
cy="1030.3542"
|
||||
cx="23.110121"
|
||||
id="circle4495"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#171311;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4497"
|
||||
d="m 5.0055377,1027.2727 c -1.170435,-1.0835 -2.026973,-0.7721 -2.044172,-0.7463"
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4.3852457,1026.9152 c -1.158557,0.036 -1.346704,0.6303 -1.33881,0.6523"
|
||||
id="path4499"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 26.630533,1027.1724 c 1.17043,-1.0835 2.02697,-0.7721 2.04417,-0.7463"
|
||||
id="path4501"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4503"
|
||||
d="m 27.321773,1026.673 c 1.15856,0.036 1.3467,0.6302 1.3388,0.6522"
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -203,7 +203,7 @@ To build without <code>cgo</code>, set the environment variable
|
||||
Change to the directory that will be its parent
|
||||
and make sure the <code>go</code> directory does not exist.
|
||||
Then clone the repository and check out the latest release tag
|
||||
(<code class="versionTag">go1.6</code>, for example):</p>
|
||||
(<code class="versionTag">go1.7.2</code>, for example):</p>
|
||||
|
||||
<pre>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
@@ -391,7 +391,7 @@ New releases are announced on the
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
Each announcement mentions the latest release tag, for instance,
|
||||
<code class="versionTag">go1.6</code>.
|
||||
<code class="versionTag">go1.7.2</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 5.6 KiB |
@@ -472,9 +472,29 @@ func escAnalyze(all []*Node, recursive bool) {
|
||||
|
||||
// visit the upstream of each dst, mark address nodes with
|
||||
// addrescapes, mark parameters unsafe
|
||||
escapes := make([]uint16, len(e.dsts))
|
||||
for i, n := range e.dsts {
|
||||
escapes[i] = n.Esc
|
||||
}
|
||||
for _, n := range e.dsts {
|
||||
escflood(e, n)
|
||||
}
|
||||
for {
|
||||
done := true
|
||||
for i, n := range e.dsts {
|
||||
if n.Esc != escapes[i] {
|
||||
done = false
|
||||
if Debug['m'] > 2 {
|
||||
Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n)
|
||||
}
|
||||
escapes[i] = n.Esc
|
||||
escflood(e, n)
|
||||
}
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// for all top level functions, tag the typenodes corresponding to the param nodes
|
||||
for _, n := range all {
|
||||
@@ -1796,6 +1816,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
|
||||
}
|
||||
|
||||
leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth
|
||||
leaks = leaks || level.int() <= 0 && dst.Esc&EscMask == EscHeap
|
||||
|
||||
osrcesc = src.Esc
|
||||
switch src.Op {
|
||||
|
||||
@@ -14,8 +14,7 @@ func dse(f *Func) {
|
||||
defer f.retSparseSet(loadUse)
|
||||
storeUse := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(storeUse)
|
||||
shadowed := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(shadowed)
|
||||
shadowed := newSparseMap(f.NumValues()) // TODO: cache
|
||||
for _, b := range f.Blocks {
|
||||
// Find all the stores in this block. Categorize their uses:
|
||||
// loadUse contains stores which are used by a subsequent load.
|
||||
@@ -81,17 +80,18 @@ func dse(f *Func) {
|
||||
shadowed.clear()
|
||||
}
|
||||
if v.Op == OpStore || v.Op == OpZero {
|
||||
if shadowed.contains(v.Args[0].ID) {
|
||||
sz := v.AuxInt
|
||||
if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz {
|
||||
// Modify store into a copy
|
||||
if v.Op == OpStore {
|
||||
// store addr value mem
|
||||
v.SetArgs1(v.Args[2])
|
||||
} else {
|
||||
// zero addr mem
|
||||
sz := v.Args[0].Type.ElemType().Size()
|
||||
if v.AuxInt != sz {
|
||||
typesz := v.Args[0].Type.ElemType().Size()
|
||||
if sz != typesz {
|
||||
f.Fatalf("mismatched zero/store sizes: %d and %d [%s]",
|
||||
v.AuxInt, sz, v.LongString())
|
||||
sz, typesz, v.LongString())
|
||||
}
|
||||
v.SetArgs1(v.Args[1])
|
||||
}
|
||||
@@ -99,7 +99,10 @@ func dse(f *Func) {
|
||||
v.AuxInt = 0
|
||||
v.Op = OpCopy
|
||||
} else {
|
||||
shadowed.add(v.Args[0].ID)
|
||||
if sz > 0x7fffffff { // work around sparseMap's int32 value type
|
||||
sz = 0x7fffffff
|
||||
}
|
||||
shadowed.set(v.Args[0].ID, int32(sz))
|
||||
}
|
||||
}
|
||||
// walk to previous store
|
||||
|
||||
@@ -8,7 +8,7 @@ import "testing"
|
||||
|
||||
func TestDeadStore(t *testing.T) {
|
||||
c := testConfig(t)
|
||||
elemType := &TypeImpl{Size_: 8, Name: "testtype"}
|
||||
elemType := &TypeImpl{Size_: 1, Name: "testtype"}
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
@@ -18,7 +18,7 @@ func TestDeadStore(t *testing.T) {
|
||||
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("addr2", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("addr3", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("zero1", OpZero, TypeMem, 8, nil, "addr3", "start"),
|
||||
Valu("zero1", OpZero, TypeMem, 1, nil, "addr3", "start"),
|
||||
Valu("store1", OpStore, TypeMem, 1, nil, "addr1", "v", "zero1"),
|
||||
Valu("store2", OpStore, TypeMem, 1, nil, "addr2", "v", "store1"),
|
||||
Valu("store3", OpStore, TypeMem, 1, nil, "addr1", "v", "store2"),
|
||||
@@ -95,3 +95,32 @@ func TestDeadStoreTypes(t *testing.T) {
|
||||
t.Errorf("store %s incorrectly removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeadStoreUnsafe(t *testing.T) {
|
||||
// Make sure a narrow store can't shadow a wider one. The test above
|
||||
// covers the case of two different types, but unsafe pointer casting
|
||||
// can get to a point where the size is changed but type unchanged.
|
||||
c := testConfig(t)
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
Valu("start", OpInitMem, TypeMem, 0, nil),
|
||||
Valu("sb", OpSB, TypeInvalid, 0, nil),
|
||||
Valu("v", OpConstBool, TypeBool, 1, nil),
|
||||
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("store1", OpStore, TypeMem, 8, nil, "addr1", "v", "start"), // store 8 bytes
|
||||
Valu("store2", OpStore, TypeMem, 1, nil, "addr1", "v", "store1"), // store 1 byte
|
||||
Goto("exit")),
|
||||
Bloc("exit",
|
||||
Exit("store2")))
|
||||
|
||||
CheckFunc(fun.f)
|
||||
cse(fun.f)
|
||||
dse(fun.f)
|
||||
CheckFunc(fun.f)
|
||||
|
||||
v := fun.values["store1"]
|
||||
if v.Op == OpCopy {
|
||||
t.Errorf("store %s incorrectly removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ func (w *Writer) Close() error {
|
||||
// the result of NewWriter or NewWriterDict called with dst
|
||||
// and w's level and dictionary.
|
||||
func (w *Writer) Reset(dst io.Writer) {
|
||||
if dw, ok := w.d.w.w.(*dictWriter); ok {
|
||||
if dw, ok := w.d.w.writer.(*dictWriter); ok {
|
||||
// w was created with NewWriterDict
|
||||
dw.w = dst
|
||||
w.d.reset(dw)
|
||||
|
||||
@@ -6,6 +6,7 @@ package flate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
@@ -631,3 +632,52 @@ func TestBestSpeed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errIO = errors.New("IO error")
|
||||
|
||||
// failWriter fails with errIO exactly at the nth call to Write.
|
||||
type failWriter struct{ n int }
|
||||
|
||||
func (w *failWriter) Write(b []byte) (int, error) {
|
||||
w.n--
|
||||
if w.n == -1 {
|
||||
return 0, errIO
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func TestWriterPersistentError(t *testing.T) {
|
||||
d, err := ioutil.ReadFile("../testdata/Mark.Twain-Tom.Sawyer.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
d = d[:10000] // Keep this test short
|
||||
|
||||
zw, err := NewWriter(nil, DefaultCompression)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
|
||||
// Sweep over the threshold at which an error is returned.
|
||||
// The variable i makes it such that the ith call to failWriter.Write will
|
||||
// return errIO. Since failWriter errors are not persistent, we must ensure
|
||||
// that flate.Writer errors are persistent.
|
||||
for i := 0; i < 1000; i++ {
|
||||
fw := &failWriter{i}
|
||||
zw.Reset(fw)
|
||||
|
||||
_, werr := zw.Write(d)
|
||||
cerr := zw.Close()
|
||||
if werr != errIO && werr != nil {
|
||||
t.Errorf("test %d, mismatching Write error: got %v, want %v", i, werr, errIO)
|
||||
}
|
||||
if cerr != errIO && fw.n < 0 {
|
||||
t.Errorf("test %d, mismatching Close error: got %v, want %v", i, cerr, errIO)
|
||||
}
|
||||
if fw.n >= 0 {
|
||||
// At this point, the failure threshold was sufficiently high enough
|
||||
// that we wrote the whole stream without any errors.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,11 @@ var offsetBase = []uint32{
|
||||
var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||
|
||||
type huffmanBitWriter struct {
|
||||
w io.Writer
|
||||
// writer is the underlying writer.
|
||||
// Do not use it directly; use the write method, which ensures
|
||||
// that Write errors are sticky.
|
||||
writer io.Writer
|
||||
|
||||
// Data waiting to be written is bytes[0:nbytes]
|
||||
// and then the low nbits of bits.
|
||||
bits uint64
|
||||
@@ -96,7 +100,7 @@ type huffmanBitWriter struct {
|
||||
|
||||
func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
|
||||
return &huffmanBitWriter{
|
||||
w: w,
|
||||
writer: w,
|
||||
literalFreq: make([]int32, maxNumLit),
|
||||
offsetFreq: make([]int32, offsetCodeCount),
|
||||
codegen: make([]uint8, maxNumLit+offsetCodeCount+1),
|
||||
@@ -107,7 +111,7 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.writer = writer
|
||||
w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil
|
||||
w.bytes = [bufferSize]byte{}
|
||||
}
|
||||
@@ -129,11 +133,21 @@ func (w *huffmanBitWriter) flush() {
|
||||
n++
|
||||
}
|
||||
w.bits = 0
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
w.nbytes = 0
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) write(b []byte) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
_, w.err = w.writer.Write(b)
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.bits |= uint64(b) << w.nbits
|
||||
w.nbits += nb
|
||||
if w.nbits >= 48 {
|
||||
@@ -150,7 +164,7 @@ func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
|
||||
bytes[5] = byte(bits >> 40)
|
||||
n += 6
|
||||
if n >= bufferFlushSize {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
n = 0
|
||||
}
|
||||
w.nbytes = n
|
||||
@@ -173,13 +187,10 @@ func (w *huffmanBitWriter) writeBytes(bytes []byte) {
|
||||
n++
|
||||
}
|
||||
if n != 0 {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.write(w.bytes[:n])
|
||||
}
|
||||
w.nbytes = 0
|
||||
_, w.err = w.w.Write(bytes)
|
||||
w.write(bytes)
|
||||
}
|
||||
|
||||
// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
|
||||
@@ -341,7 +352,7 @@ func (w *huffmanBitWriter) writeCode(c hcode) {
|
||||
bytes[5] = byte(bits >> 40)
|
||||
n += 6
|
||||
if n >= bufferFlushSize {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
n = 0
|
||||
}
|
||||
w.nbytes = n
|
||||
@@ -572,6 +583,9 @@ func (w *huffmanBitWriter) indexTokens(tokens []token) (numLiterals, numOffsets
|
||||
// writeTokens writes a slice of tokens to the output.
|
||||
// codes for literal and offset encoding must be supplied.
|
||||
func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
for _, t := range tokens {
|
||||
if t < matchType {
|
||||
w.writeCode(leCodes[t.literal()])
|
||||
@@ -676,9 +690,9 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
|
||||
if n < bufferFlushSize {
|
||||
continue
|
||||
}
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
if w.err != nil {
|
||||
return
|
||||
return // Return early in the event of write failures
|
||||
}
|
||||
n = 0
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ func (x *cbc) CryptBlocks(dst, src []byte) {
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
}
|
||||
cryptBlocksChain(x.c, &x.iv[0], &x.b.key[0], &dst[0], &src[0], len(src))
|
||||
if len(src) > 0 {
|
||||
cryptBlocksChain(x.c, &x.iv[0], &x.b.key[0], &dst[0], &src[0], len(src))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *cbc) SetIV(iv []byte) {
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
package cipher_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -34,3 +36,55 @@ func mustPanic(t *testing.T, msg string, f func()) {
|
||||
}()
|
||||
f()
|
||||
}
|
||||
|
||||
func TestEmptyPlaintext(t *testing.T) {
|
||||
var key [16]byte
|
||||
a, err := aes.NewCipher(key[:16])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d, err := des.NewCipher(key[:8])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := 16
|
||||
pt := make([]byte, s)
|
||||
ct := make([]byte, s)
|
||||
for i := 0; i < 16; i++ {
|
||||
pt[i], ct[i] = byte(i), byte(i)
|
||||
}
|
||||
|
||||
assertEqual := func(name string, got, want []byte) {
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("%s: got %v, want %v", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
for _, b := range []cipher.Block{a, d} {
|
||||
iv := make([]byte, b.BlockSize())
|
||||
cbce := cipher.NewCBCEncrypter(b, iv)
|
||||
cbce.CryptBlocks(ct, pt[:0])
|
||||
assertEqual("CBC encrypt", ct, pt)
|
||||
|
||||
cbcd := cipher.NewCBCDecrypter(b, iv)
|
||||
cbcd.CryptBlocks(ct, pt[:0])
|
||||
assertEqual("CBC decrypt", ct, pt)
|
||||
|
||||
cfbe := cipher.NewCFBEncrypter(b, iv)
|
||||
cfbe.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("CFB encrypt", ct, pt)
|
||||
|
||||
cfbd := cipher.NewCFBDecrypter(b, iv)
|
||||
cfbd.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("CFB decrypt", ct, pt)
|
||||
|
||||
ctr := cipher.NewCTR(b, iv)
|
||||
ctr.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("CTR", ct, pt)
|
||||
|
||||
ofb := cipher.NewOFB(b, iv)
|
||||
ofb.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("OFB", ct, pt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,14 @@ type Conn struct {
|
||||
|
||||
// constant after handshake; protected by handshakeMutex
|
||||
handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
|
||||
handshakeErr error // error resulting from handshake
|
||||
vers uint16 // TLS version
|
||||
haveVers bool // version has been negotiated
|
||||
config *Config // configuration passed to constructor
|
||||
// handshakeCond, if not nil, indicates that a goroutine is committed
|
||||
// to running the handshake for this Conn. Other goroutines that need
|
||||
// to wait for the handshake can wait on this, under handshakeMutex.
|
||||
handshakeCond *sync.Cond
|
||||
handshakeErr error // error resulting from handshake
|
||||
vers uint16 // TLS version
|
||||
haveVers bool // version has been negotiated
|
||||
config *Config // configuration passed to constructor
|
||||
// handshakeComplete is true if the connection is currently transfering
|
||||
// application data (i.e. is not currently processing a handshake).
|
||||
handshakeComplete bool
|
||||
@@ -1206,26 +1210,50 @@ func (c *Conn) Handshake() error {
|
||||
// need to check whether a handshake is pending (such as Write) to
|
||||
// block.
|
||||
//
|
||||
// Thus we take c.handshakeMutex first and, if we find that a handshake
|
||||
// is needed, then we unlock, acquire c.in and c.handshakeMutex in the
|
||||
// correct order, and check again.
|
||||
// Thus we first take c.handshakeMutex to check whether a handshake is
|
||||
// needed.
|
||||
//
|
||||
// If so then, previously, this code would unlock handshakeMutex and
|
||||
// then lock c.in and handshakeMutex in the correct order to run the
|
||||
// handshake. The problem was that it was possible for a Read to
|
||||
// complete the handshake once handshakeMutex was unlocked and then
|
||||
// keep c.in while waiting for network data. Thus a concurrent
|
||||
// operation could be blocked on c.in.
|
||||
//
|
||||
// Thus handshakeCond is used to signal that a goroutine is committed
|
||||
// to running the handshake and other goroutines can wait on it if they
|
||||
// need. handshakeCond is protected by handshakeMutex.
|
||||
c.handshakeMutex.Lock()
|
||||
defer c.handshakeMutex.Unlock()
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
if i == 1 {
|
||||
c.handshakeMutex.Unlock()
|
||||
c.in.Lock()
|
||||
defer c.in.Unlock()
|
||||
c.handshakeMutex.Lock()
|
||||
}
|
||||
|
||||
for {
|
||||
if err := c.handshakeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
if c.handshakeComplete {
|
||||
return nil
|
||||
}
|
||||
if c.handshakeCond == nil {
|
||||
break
|
||||
}
|
||||
|
||||
c.handshakeCond.Wait()
|
||||
}
|
||||
|
||||
// Set handshakeCond to indicate that this goroutine is committing to
|
||||
// running the handshake.
|
||||
c.handshakeCond = sync.NewCond(&c.handshakeMutex)
|
||||
c.handshakeMutex.Unlock()
|
||||
|
||||
c.in.Lock()
|
||||
defer c.in.Unlock()
|
||||
|
||||
c.handshakeMutex.Lock()
|
||||
|
||||
// The handshake cannot have completed when handshakeMutex was unlocked
|
||||
// because this goroutine set handshakeCond.
|
||||
if c.handshakeErr != nil || c.handshakeComplete {
|
||||
panic("handshake should not have been able to complete after handshakeCond was set")
|
||||
}
|
||||
|
||||
if c.isClient {
|
||||
@@ -1236,6 +1264,16 @@ func (c *Conn) Handshake() error {
|
||||
if c.handshakeErr == nil {
|
||||
c.handshakes++
|
||||
}
|
||||
|
||||
if c.handshakeErr == nil && !c.handshakeComplete {
|
||||
panic("handshake should have had a result.")
|
||||
}
|
||||
|
||||
// Wake any other goroutines that are waiting for this handshake to
|
||||
// complete.
|
||||
c.handshakeCond.Broadcast()
|
||||
c.handshakeCond = nil
|
||||
|
||||
return c.handshakeErr
|
||||
}
|
||||
|
||||
|
||||
@@ -1045,3 +1045,57 @@ func TestBuffering(t *testing.T) {
|
||||
t.Errorf("expected server handshake to complete with only two writes, but saw %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandshakeRace(t *testing.T) {
|
||||
// This test races a Read and Write to try and complete a handshake in
|
||||
// order to provide some evidence that there are no races or deadlocks
|
||||
// in the handshake locking.
|
||||
for i := 0; i < 32; i++ {
|
||||
c, s := net.Pipe()
|
||||
|
||||
go func() {
|
||||
server := Server(s, testConfig)
|
||||
if err := server.Handshake(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var request [1]byte
|
||||
if n, err := server.Read(request[:]); err != nil || n != 1 {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
server.Write(request[:])
|
||||
server.Close()
|
||||
}()
|
||||
|
||||
startWrite := make(chan struct{})
|
||||
startRead := make(chan struct{})
|
||||
readDone := make(chan struct{})
|
||||
|
||||
client := Client(c, testConfig)
|
||||
go func() {
|
||||
<-startWrite
|
||||
var request [1]byte
|
||||
client.Write(request[:])
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-startRead
|
||||
var reply [1]byte
|
||||
if n, err := client.Read(reply[:]); err != nil || n != 1 {
|
||||
panic(err)
|
||||
}
|
||||
c.Close()
|
||||
readDone <- struct{}{}
|
||||
}()
|
||||
|
||||
if i&1 == 1 {
|
||||
startWrite <- struct{}{}
|
||||
startRead <- struct{}{}
|
||||
} else {
|
||||
startRead <- struct{}{}
|
||||
startWrite <- struct{}{}
|
||||
}
|
||||
<-readDone
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@
|
||||
|
||||
package crc32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
vxMinLen = 64
|
||||
vxAlignment = 16
|
||||
vxAlignMask = vxAlignment - 1
|
||||
vxAlignMask = 15 // align to 16 bytes
|
||||
)
|
||||
|
||||
// hasVectorFacility reports whether the machine has the z/Architecture
|
||||
@@ -49,20 +44,13 @@ func genericIEEE(crc uint32, p []byte) uint32 {
|
||||
return update(crc, IEEETable, p)
|
||||
}
|
||||
|
||||
// updateCastagnoli calculates the checksum of p using genericCastagnoli to
|
||||
// align the data appropriately for vectorCastagnoli. It avoids using
|
||||
// vectorCastagnoli entirely if the length of p is less than or equal to
|
||||
// vxMinLen.
|
||||
// updateCastagnoli calculates the checksum of p using
|
||||
// vectorizedCastagnoli if possible and falling back onto
|
||||
// genericCastagnoli as needed.
|
||||
func updateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
// Use vectorized function if vector facility is available and
|
||||
// data length is above threshold.
|
||||
if hasVX && len(p) > vxMinLen {
|
||||
pAddr := uintptr(unsafe.Pointer(&p[0]))
|
||||
if pAddr&vxAlignMask != 0 {
|
||||
prealign := vxAlignment - int(pAddr&vxAlignMask)
|
||||
crc = genericCastagnoli(crc, p[:prealign])
|
||||
p = p[prealign:]
|
||||
}
|
||||
if hasVX && len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedCastagnoli(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
@@ -75,19 +63,12 @@ func updateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
return genericCastagnoli(crc, p)
|
||||
}
|
||||
|
||||
// updateIEEE calculates the checksum of p using genericIEEE to align the data
|
||||
// appropriately for vectorIEEE. It avoids using vectorIEEE entirely if the length
|
||||
// of p is less than or equal to vxMinLen.
|
||||
// updateIEEE calculates the checksum of p using vectorizedIEEE if
|
||||
// possible and falling back onto genericIEEE as needed.
|
||||
func updateIEEE(crc uint32, p []byte) uint32 {
|
||||
// Use vectorized function if vector facility is available and
|
||||
// data length is above threshold.
|
||||
if hasVX && len(p) > vxMinLen {
|
||||
pAddr := uintptr(unsafe.Pointer(&p[0]))
|
||||
if pAddr&vxAlignMask != 0 {
|
||||
prealign := vxAlignment - int(pAddr&vxAlignMask)
|
||||
crc = genericIEEE(crc, p[:prealign])
|
||||
p = p[prealign:]
|
||||
}
|
||||
if hasVX && len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedIEEE(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
|
||||
@@ -128,6 +128,10 @@ TEXT vectorizedBody<>(SB),NOSPLIT,$0
|
||||
VZERO V0
|
||||
VLVGF $3, R2, V0
|
||||
|
||||
// Crash if the input size is less than 64-bytes.
|
||||
CMP R4, $64
|
||||
BLT crash
|
||||
|
||||
// Load a 64-byte data chunk and XOR with CRC
|
||||
VLM 0(R3), V1, V4 // 64-bytes into V1..V4
|
||||
|
||||
@@ -243,3 +247,6 @@ done:
|
||||
XOR $0xffffffff, R2 // NOTW R2
|
||||
MOVWZ R2, ret + 32(FP)
|
||||
RET
|
||||
|
||||
crash:
|
||||
MOVD $0, (R0) // input size is less than 64-bytes
|
||||
|
||||
@@ -18,15 +18,16 @@ func (mr *multiReader) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
}
|
||||
n, err = mr.readers[0].Read(p)
|
||||
if err == EOF {
|
||||
mr.readers = mr.readers[1:]
|
||||
}
|
||||
if n > 0 || err != EOF {
|
||||
if err == EOF {
|
||||
// Don't return EOF yet. There may be more bytes
|
||||
// in the remaining readers.
|
||||
if err == EOF && len(mr.readers) > 0 {
|
||||
// Don't return EOF yet. More readers remain.
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
mr.readers = mr.readers[1:]
|
||||
}
|
||||
return 0, EOF
|
||||
}
|
||||
|
||||
@@ -196,3 +196,41 @@ func TestMultiReaderFlatten(t *testing.T) {
|
||||
myDepth+2, readDepth)
|
||||
}
|
||||
}
|
||||
|
||||
// byteAndEOFReader is a Reader which reads one byte (the underlying
|
||||
// byte) and io.EOF at once in its Read call.
|
||||
type byteAndEOFReader byte
|
||||
|
||||
func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
// Read(0 bytes) is useless. We expect no such useless
|
||||
// calls in this test.
|
||||
panic("unexpected call")
|
||||
}
|
||||
p[0] = byte(b)
|
||||
return 1, EOF
|
||||
}
|
||||
|
||||
// In Go 1.7, this yielded bytes forever.
|
||||
func TestMultiReaderSingleByteWithEOF(t *testing.T) {
|
||||
got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const want = "ab"
|
||||
if string(got) != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a reader returning (n, EOF) at the end of an MultiReader
|
||||
// chain continues to return EOF on its final read, rather than
|
||||
// yielding a (0, EOF).
|
||||
func TestMultiReaderFinalEOF(t *testing.T) {
|
||||
r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
|
||||
buf := make([]byte, 2)
|
||||
n, err := r.Read(buf)
|
||||
if n != 1 || err != EOF {
|
||||
t.Errorf("got %v, %v; want 1, EOF", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func (d *Dialer) dialDNS(ctx context.Context, network, server string) (dnsConn,
|
||||
}
|
||||
|
||||
// exchange sends a query on the connection and hopes for a response.
|
||||
func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg, error) {
|
||||
func exchange(ctx context.Context, server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
|
||||
d := testHookDNSDialer()
|
||||
out := dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
@@ -152,6 +152,12 @@ func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg,
|
||||
},
|
||||
}
|
||||
for _, network := range []string{"udp", "tcp"} {
|
||||
// TODO(mdempsky): Refactor so defers from UDP-based
|
||||
// exchanges happen before TCP-based exchange.
|
||||
|
||||
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
|
||||
defer cancel()
|
||||
|
||||
c, err := d.dialDNS(ctx, network, server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -180,17 +186,10 @@ func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16)
|
||||
return "", nil, &DNSError{Err: "no DNS servers", Name: name}
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(cfg.timeout)
|
||||
if old, ok := ctx.Deadline(); !ok || deadline.Before(old) {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, deadline)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for i := 0; i < cfg.attempts; i++ {
|
||||
for _, server := range cfg.servers {
|
||||
msg, err := exchange(ctx, server, name, qtype)
|
||||
msg, err := exchange(ctx, server, name, qtype, cfg.timeout)
|
||||
if err != nil {
|
||||
lastErr = &DNSError{
|
||||
Err: err.Error(),
|
||||
@@ -338,8 +337,9 @@ func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs [
|
||||
}
|
||||
|
||||
// avoidDNS reports whether this is a hostname for which we should not
|
||||
// use DNS. Currently this includes only .onion and .local names,
|
||||
// per RFC 7686 and RFC 6762, respectively. See golang.org/issue/13705.
|
||||
// use DNS. Currently this includes only .onion, per RFC 7686. See
|
||||
// golang.org/issue/13705. Does not cover .local names (RFC 6762),
|
||||
// see golang.org/issue/16739.
|
||||
func avoidDNS(name string) bool {
|
||||
if name == "" {
|
||||
return true
|
||||
@@ -347,7 +347,7 @@ func avoidDNS(name string) bool {
|
||||
if name[len(name)-1] == '.' {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
return stringsHasSuffixFold(name, ".onion") || stringsHasSuffixFold(name, ".local")
|
||||
return stringsHasSuffixFold(name, ".onion")
|
||||
}
|
||||
|
||||
// nameList returns a list of names for sequential DNS queries.
|
||||
|
||||
@@ -40,9 +40,9 @@ func TestDNSTransportFallback(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
for _, tt := range dnsTransportFallbackTests {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tt.timeout)*time.Second)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
msg, err := exchange(ctx, tt.server, tt.name, tt.qtype)
|
||||
msg, err := exchange(ctx, tt.server, tt.name, tt.qtype, time.Second)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
@@ -82,9 +82,9 @@ func TestSpecialDomainName(t *testing.T) {
|
||||
|
||||
server := "8.8.8.8:53"
|
||||
for _, tt := range specialDomainNameTests {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
msg, err := exchange(ctx, server, tt.name, tt.qtype)
|
||||
msg, err := exchange(ctx, server, tt.name, tt.qtype, 3*time.Second)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
@@ -112,10 +112,11 @@ func TestAvoidDNSName(t *testing.T) {
|
||||
{"foo.ONION", true},
|
||||
{"foo.ONION.", true},
|
||||
|
||||
{"foo.local.", true},
|
||||
{"foo.local", true},
|
||||
{"foo.LOCAL", true},
|
||||
{"foo.LOCAL.", true},
|
||||
// But do resolve *.local address; Issue 16739
|
||||
{"foo.local.", false},
|
||||
{"foo.local", false},
|
||||
{"foo.LOCAL", false},
|
||||
{"foo.LOCAL.", false},
|
||||
|
||||
{"", true}, // will be rejected earlier too
|
||||
|
||||
@@ -500,7 +501,7 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
|
||||
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
id: q.id,
|
||||
@@ -539,14 +540,15 @@ func TestIgnoreLameReferrals(t *testing.T) {
|
||||
}
|
||||
defer conf.teardown()
|
||||
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", "nameserver 192.0.2.2"}); err != nil {
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
|
||||
"nameserver 192.0.2.2"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
|
||||
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
|
||||
t.Log(s, q)
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
@@ -633,28 +635,30 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
|
||||
|
||||
type fakeDNSDialer struct {
|
||||
// reply handler
|
||||
rh func(s string, q *dnsMsg) (*dnsMsg, error)
|
||||
rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
|
||||
}
|
||||
|
||||
func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) {
|
||||
return &fakeDNSConn{f.rh, s}, nil
|
||||
return &fakeDNSConn{f.rh, s, time.Time{}}, nil
|
||||
}
|
||||
|
||||
type fakeDNSConn struct {
|
||||
rh func(s string, q *dnsMsg) (*dnsMsg, error)
|
||||
rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
|
||||
s string
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) SetDeadline(time.Time) error {
|
||||
func (f *fakeDNSConn) SetDeadline(t time.Time) error {
|
||||
f.t = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) {
|
||||
return f.rh(f.s, q)
|
||||
return f.rh(f.s, q, f.t)
|
||||
}
|
||||
|
||||
// UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
|
||||
@@ -724,3 +728,72 @@ func TestIgnoreDNSForgeries(t *testing.T) {
|
||||
t.Errorf("got address %v, want %v", got, TestAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16865. If a name server times out, continue to the next.
|
||||
func TestRetryTimeout(t *testing.T) {
|
||||
origTestHookDNSDialer := testHookDNSDialer
|
||||
defer func() { testHookDNSDialer = origTestHookDNSDialer }()
|
||||
|
||||
conf, err := newResolvConfTest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conf.teardown()
|
||||
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will timeout
|
||||
"nameserver 192.0.2.2"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
var deadline0 time.Time
|
||||
|
||||
d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
|
||||
t.Log(s, q, deadline)
|
||||
|
||||
if deadline.IsZero() {
|
||||
t.Error("zero deadline")
|
||||
}
|
||||
|
||||
if s == "192.0.2.1:53" {
|
||||
deadline0 = deadline
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return nil, errTimeout
|
||||
}
|
||||
|
||||
if deadline == deadline0 {
|
||||
t.Error("deadline didn't change")
|
||||
}
|
||||
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
id: q.id,
|
||||
response: true,
|
||||
recursion_available: true,
|
||||
},
|
||||
question: q.question,
|
||||
answer: []dnsRR{
|
||||
&dnsRR_CNAME{
|
||||
Hdr: dnsRR_Header{
|
||||
Name: q.question[0].Name,
|
||||
Rrtype: dnsTypeCNAME,
|
||||
Class: dnsClassINET,
|
||||
},
|
||||
Cname: "golang.org",
|
||||
},
|
||||
},
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
_, err = goLookupCNAME(context.Background(), "www.golang.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if deadline0.IsZero() {
|
||||
t.Error("deadline0 still zero", deadline0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ func TestH12_RequestContentLength_Known_NonZero(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestH12_RequestContentLength_Known_Zero(t *testing.T) {
|
||||
h12requestContentLength(t, func() io.Reader { return strings.NewReader("") }, 0)
|
||||
h12requestContentLength(t, func() io.Reader { return nil }, 0)
|
||||
}
|
||||
|
||||
func TestH12_RequestContentLength_Unknown(t *testing.T) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle.
|
||||
//go:generate bundle -o h2_bundle.go -prefix http2 golang.org/x/net/http2
|
||||
//go:generate bundle -o h2_bundle.go -prefix http2 -underscore golang.org/x/net/http2
|
||||
|
||||
// Package http2 implements the HTTP/2 protocol.
|
||||
//
|
||||
@@ -5448,28 +5448,17 @@ func http2checkConnHeaders(req *Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func http2bodyAndLength(req *Request) (body io.Reader, contentLen int64) {
|
||||
body = req.Body
|
||||
if body == nil {
|
||||
return nil, 0
|
||||
// actualContentLength returns a sanitized version of
|
||||
// req.ContentLength, where 0 actually means zero (not unknown) and -1
|
||||
// means unknown.
|
||||
func http2actualContentLength(req *Request) int64 {
|
||||
if req.Body == nil {
|
||||
return 0
|
||||
}
|
||||
if req.ContentLength != 0 {
|
||||
return req.Body, req.ContentLength
|
||||
return req.ContentLength
|
||||
}
|
||||
|
||||
// We have a body but a zero content length. Test to see if
|
||||
// it's actually zero or just unset.
|
||||
var buf [1]byte
|
||||
n, rerr := io.ReadFull(body, buf[:])
|
||||
if rerr != nil && rerr != io.EOF {
|
||||
return http2errorReader{rerr}, -1
|
||||
}
|
||||
if n == 1 {
|
||||
|
||||
return io.MultiReader(bytes.NewReader(buf[:]), body), -1
|
||||
}
|
||||
|
||||
return nil, 0
|
||||
return -1
|
||||
}
|
||||
|
||||
func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
|
||||
@@ -5483,9 +5472,6 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
|
||||
}
|
||||
hasTrailers := trailers != ""
|
||||
|
||||
body, contentLen := http2bodyAndLength(req)
|
||||
hasBody := body != nil
|
||||
|
||||
cc.mu.Lock()
|
||||
cc.lastActive = time.Now()
|
||||
if cc.closed || !cc.canTakeNewRequestLocked() {
|
||||
@@ -5493,6 +5479,10 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
|
||||
return nil, http2errClientConnUnusable
|
||||
}
|
||||
|
||||
body := req.Body
|
||||
hasBody := body != nil
|
||||
contentLen := http2actualContentLength(req)
|
||||
|
||||
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
|
||||
var requestedGzip bool
|
||||
if !cc.t.disableCompression() &&
|
||||
@@ -5714,8 +5704,13 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos
|
||||
}
|
||||
}
|
||||
|
||||
if sentEnd {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var trls []byte
|
||||
if !sentEnd && hasTrailers {
|
||||
if hasTrailers {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
trls = cc.encodeTrailers(req)
|
||||
|
||||
@@ -498,8 +498,9 @@ func (t *Transport) CloseIdleConnections() {
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
// CancelRequest should only be called after RoundTrip has returned.
|
||||
//
|
||||
// Deprecated: Use Request.Cancel instead. CancelRequest cannot cancel
|
||||
// HTTP/2 requests.
|
||||
// Deprecated: Use Request.WithContext to create a request with a
|
||||
// cancelable context instead. CancelRequest cannot cancel HTTP/2
|
||||
// requests.
|
||||
func (t *Transport) CancelRequest(req *Request) {
|
||||
t.reqMu.Lock()
|
||||
cancel := t.reqCanceler[req]
|
||||
@@ -589,6 +590,7 @@ var (
|
||||
errReadLoopExiting = errors.New("http: persistConn.readLoop exiting")
|
||||
errServerClosedIdle = errors.New("http: server closed idle connection")
|
||||
errIdleConnTimeout = errors.New("http: idle connection timeout")
|
||||
errNotCachingH2Conn = errors.New("http: not caching alternate protocol's connections")
|
||||
)
|
||||
|
||||
// transportReadFromServerError is used by Transport.readLoop when the
|
||||
@@ -632,6 +634,9 @@ func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
|
||||
if pconn.isBroken() {
|
||||
return errConnBroken
|
||||
}
|
||||
if pconn.alt != nil {
|
||||
return errNotCachingH2Conn
|
||||
}
|
||||
pconn.markReused()
|
||||
key := pconn.cacheKey
|
||||
|
||||
|
||||
@@ -3511,6 +3511,61 @@ func TestTransportIdleConnTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16208: Go 1.7 crashed after Transport.IdleConnTimeout if an
|
||||
// HTTP/2 connection was established but but its caller no longer
|
||||
// wanted it. (Assuming the connection cache was enabled, which it is
|
||||
// by default)
|
||||
//
|
||||
// This test reproduced the crash by setting the IdleConnTimeout low
|
||||
// (to make the test reasonable) and then making a request which is
|
||||
// canceled by the DialTLS hook, which then also waits to return the
|
||||
// real connection until after the RoundTrip saw the error. Then we
|
||||
// know the successful tls.Dial from DialTLS will need to go into the
|
||||
// idle pool. Then we give it a of time to explode.
|
||||
func TestIdleConnH2Crash(t *testing.T) {
|
||||
cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
// nothing
|
||||
}))
|
||||
defer cst.close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
gotErr := make(chan bool, 1)
|
||||
|
||||
cst.tr.IdleConnTimeout = 5 * time.Millisecond
|
||||
cst.tr.DialTLS = func(network, addr string) (net.Conn, error) {
|
||||
cancel()
|
||||
<-gotErr
|
||||
c, err := tls.Dial(network, addr, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
if cs := c.ConnectionState(); cs.NegotiatedProtocol != "h2" {
|
||||
t.Errorf("protocol = %q; want %q", cs.NegotiatedProtocol, "h2")
|
||||
c.Close()
|
||||
return nil, errors.New("bogus")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
req, _ := NewRequest("GET", cst.ts.URL, nil)
|
||||
req = req.WithContext(ctx)
|
||||
res, err := cst.c.Do(req)
|
||||
if err == nil {
|
||||
res.Body.Close()
|
||||
t.Fatal("unexpected success")
|
||||
}
|
||||
gotErr <- true
|
||||
|
||||
// Wait for the explosion.
|
||||
time.Sleep(cst.tr.IdleConnTimeout * 10)
|
||||
}
|
||||
|
||||
type funcConn struct {
|
||||
net.Conn
|
||||
read func([]byte) (int, error)
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
|
||||
package filepath
|
||||
|
||||
var ToNorm = toNorm
|
||||
var (
|
||||
ToNorm = toNorm
|
||||
NormBase = normBase
|
||||
)
|
||||
|
||||
@@ -840,7 +840,7 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
if p, err := filepath.EvalSymlinks(path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(dest) {
|
||||
t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
|
||||
}
|
||||
|
||||
// test EvalSymlinks(".")
|
||||
@@ -872,6 +872,34 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want)
|
||||
}()
|
||||
|
||||
// test EvalSymlinks(".."+path)
|
||||
func() {
|
||||
defer func() {
|
||||
err := os.Chdir(wd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err := os.Chdir(simpleJoin(tmpDir, "test"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
path := simpleJoin("..", d.path)
|
||||
dest := simpleJoin("..", d.dest)
|
||||
if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) {
|
||||
dest = d.dest
|
||||
}
|
||||
|
||||
if p, err := filepath.EvalSymlinks(path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(dest) {
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
|
||||
}
|
||||
}()
|
||||
|
||||
// test EvalSymlinks where parameter is relative path
|
||||
func() {
|
||||
defer func() {
|
||||
@@ -889,7 +917,7 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
if p, err := filepath.EvalSymlinks(d.path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(d.dest) {
|
||||
t.Errorf("Clean(%q)=%q, want %q", d.path, p, d.dest)
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", d.path, p, d.dest)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -329,9 +329,106 @@ func TestToNorm(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
got, err := filepath.ToNorm(test.arg, stubBase)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected toNorm error, arg: %s, err: %v\n", test.arg, err)
|
||||
t.Errorf("toNorm(%s) failed: %v\n", test.arg, err)
|
||||
} else if got != test.want {
|
||||
t.Errorf("toNorm error, arg: %s, want: %s, got: %s\n", test.arg, test.want, got)
|
||||
t.Errorf("toNorm(%s) returns %s, but %s expected\n", test.arg, got, test.want)
|
||||
}
|
||||
}
|
||||
|
||||
testPath := `{{tmp}}\test\foo\bar`
|
||||
|
||||
testsDir := []struct {
|
||||
wd string
|
||||
arg string
|
||||
want string
|
||||
}{
|
||||
// test absolute paths
|
||||
{".", `{{tmp}}\test\foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\.\test/foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\test\..\test\foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\TEST\FOO\BAR`, `{{tmp}}\test\foo\bar`},
|
||||
|
||||
// test relative paths begin with drive letter
|
||||
{`{{tmp}}\test`, `{{tmpvol}}.`, `{{tmpvol}}.`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}..`, `{{tmpvol}}..`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}.\foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}foo\..\foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}FOO\BAR`, `{{tmpvol}}foo\bar`},
|
||||
|
||||
// test relative paths begin with '\'
|
||||
{".", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
|
||||
|
||||
// test relative paths begin without '\'
|
||||
{`{{tmp}}\test`, ".", `.`},
|
||||
{`{{tmp}}\test`, "..", `..`},
|
||||
{`{{tmp}}\test`, `foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `.\foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `foo\..\foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `FOO\BAR`, `foo\bar`},
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.Chdir(cwd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
tmp, err := ioutil.TempDir("", "testToNorm")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
// ioutil.TempDir might return "non-canonical" name.
|
||||
tmp, err = filepath.EvalSymlinks(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(strings.Replace(testPath, "{{tmp}}", tmp, -1), 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tmpVol := filepath.VolumeName(tmp)
|
||||
tmpNoVol := tmp[len(tmpVol):]
|
||||
|
||||
for _, test := range testsDir {
|
||||
wd := strings.Replace(strings.Replace(strings.Replace(test.wd, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
arg := strings.Replace(strings.Replace(strings.Replace(test.arg, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
want := strings.Replace(strings.Replace(strings.Replace(test.want, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
|
||||
if test.wd == "." {
|
||||
err := os.Chdir(cwd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
err := os.Chdir(wd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
got, err := filepath.ToNorm(arg, filepath.NormBase)
|
||||
if err != nil {
|
||||
t.Errorf("toNorm(%s) failed: %v (wd=%s)\n", arg, err, wd)
|
||||
} else if got != want {
|
||||
t.Errorf("toNorm(%s) returns %s, but %s expected (wd=%s)\n", arg, got, want, wd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func normVolumeName(path string) string {
|
||||
return strings.ToUpper(volume)
|
||||
}
|
||||
|
||||
// normBase retruns the last element of path.
|
||||
// normBase returns the last element of path with correct case.
|
||||
func normBase(path string) (string, error) {
|
||||
p, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
@@ -40,7 +40,24 @@ func normBase(path string) (string, error) {
|
||||
return syscall.UTF16ToString(data.FileName[:]), nil
|
||||
}
|
||||
|
||||
func toNorm(path string, base func(string) (string, error)) (string, error) {
|
||||
// baseIsDotDot returns whether the last element of path is "..".
|
||||
// The given path should be 'Clean'-ed in advance.
|
||||
func baseIsDotDot(path string) bool {
|
||||
i := strings.LastIndexByte(path, Separator)
|
||||
return path[i+1:] == ".."
|
||||
}
|
||||
|
||||
// toNorm returns the normalized path that is guranteed to be unique.
|
||||
// It should accept the following formats:
|
||||
// * UNC paths (e.g \\server\share\foo\bar)
|
||||
// * absolute paths (e.g C:\foo\bar)
|
||||
// * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
|
||||
// * relative paths begin with '\' (e.g \foo\bar)
|
||||
// * relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .)
|
||||
// The returned normalized path will be in the same form (of 5 listed above) as the input path.
|
||||
// If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B).
|
||||
// The normBase parameter should be equal to the normBase func, except for in tests. See docs on the normBase func.
|
||||
func toNorm(path string, normBase func(string) (string, error)) (string, error) {
|
||||
if path == "" {
|
||||
return path, nil
|
||||
}
|
||||
@@ -58,7 +75,13 @@ func toNorm(path string, base func(string) (string, error)) (string, error) {
|
||||
var normPath string
|
||||
|
||||
for {
|
||||
name, err := base(volume + path)
|
||||
if baseIsDotDot(path) {
|
||||
normPath = path + `\` + normPath
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
name, err := normBase(volume + path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -5720,6 +5720,8 @@ func TestTypeStrings(t *testing.T) {
|
||||
{TypeOf(new(XM)), "*reflect_test.XM"},
|
||||
{TypeOf(new(XM).String), "func() string"},
|
||||
{TypeOf(new(XM)).Method(0).Type, "func(*reflect_test.XM) string"},
|
||||
{ChanOf(3, TypeOf(XM{})), "chan reflect_test.XM"},
|
||||
{MapOf(TypeOf(int(0)), TypeOf(XM{})), "map[int]reflect_test.XM"},
|
||||
}
|
||||
|
||||
for i, test := range stringTests {
|
||||
|
||||
@@ -1848,6 +1848,7 @@ func ChanOf(dir ChanDir, t Type) Type {
|
||||
prototype := *(**chanType)(unsafe.Pointer(&ichan))
|
||||
ch := new(chanType)
|
||||
*ch = *prototype
|
||||
ch.tflag = 0
|
||||
ch.dir = uintptr(dir)
|
||||
ch.str = resolveReflectName(newName(s, "", "", false))
|
||||
ch.hash = fnv1(typ.hash, 'c', byte(dir))
|
||||
@@ -1892,6 +1893,7 @@ func MapOf(key, elem Type) Type {
|
||||
mt := new(mapType)
|
||||
*mt = **(**mapType)(unsafe.Pointer(&imap))
|
||||
mt.str = resolveReflectName(newName(s, "", "", false))
|
||||
mt.tflag = 0
|
||||
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
|
||||
mt.key = ktyp
|
||||
mt.elem = etyp
|
||||
|
||||
@@ -20,6 +20,7 @@ TEXT runtime·checkvectorfacility(SB),NOSPLIT,$32-0
|
||||
MOVD $2, R0
|
||||
MOVD R1, tmp-32(SP)
|
||||
MOVD $x-24(SP), R1
|
||||
XC $24, 0(R1), 0(R1)
|
||||
// STFLE 0(R1)
|
||||
WORD $0xB2B01000
|
||||
MOVBZ z-8(SP), R1
|
||||
|
||||
@@ -23,6 +23,7 @@ package runtime
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/umtx.h>
|
||||
#include <sys/_umtx.h>
|
||||
#include <sys/rtprio.h>
|
||||
#include <sys/thr.h>
|
||||
#include <sys/_sigset.h>
|
||||
@@ -49,6 +50,8 @@ const (
|
||||
SA_RESTART = C.SA_RESTART
|
||||
SA_ONSTACK = C.SA_ONSTACK
|
||||
|
||||
CLOCK_MONOTONIC = C.CLOCK_MONOTONIC
|
||||
|
||||
UMTX_OP_WAIT_UINT = C.UMTX_OP_WAIT_UINT
|
||||
UMTX_OP_WAIT_UINT_PRIVATE = C.UMTX_OP_WAIT_UINT_PRIVATE
|
||||
UMTX_OP_WAKE = C.UMTX_OP_WAKE
|
||||
@@ -130,4 +133,6 @@ type Timespec C.struct_timespec
|
||||
type Timeval C.struct_timeval
|
||||
type Itimerval C.struct_itimerval
|
||||
|
||||
type Umtx_time C.struct__umtx_time
|
||||
|
||||
type Kevent C.struct_kevent
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
_SA_RESTART = 0x2
|
||||
_SA_ONSTACK = 0x1
|
||||
|
||||
_CLOCK_MONOTONIC = 0x4
|
||||
|
||||
_UMTX_OP_WAIT_UINT = 0xb
|
||||
_UMTX_OP_WAIT_UINT_PRIVATE = 0xf
|
||||
_UMTX_OP_WAKE = 0x3
|
||||
@@ -203,6 +205,12 @@ type itimerval struct {
|
||||
it_value timeval
|
||||
}
|
||||
|
||||
type umtx_time struct {
|
||||
_timeout timespec
|
||||
_flags uint32
|
||||
_clockid uint32
|
||||
}
|
||||
|
||||
type keventt struct {
|
||||
ident uint32
|
||||
filter int16
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
_SA_RESTART = 0x2
|
||||
_SA_ONSTACK = 0x1
|
||||
|
||||
_CLOCK_MONOTONIC = 0x4
|
||||
|
||||
_UMTX_OP_WAIT_UINT = 0xb
|
||||
_UMTX_OP_WAIT_UINT_PRIVATE = 0xf
|
||||
_UMTX_OP_WAKE = 0x3
|
||||
@@ -214,6 +216,12 @@ type itimerval struct {
|
||||
it_value timeval
|
||||
}
|
||||
|
||||
type umtx_time struct {
|
||||
_timeout timespec
|
||||
_flags uint32
|
||||
_clockid uint32
|
||||
}
|
||||
|
||||
type keventt struct {
|
||||
ident uint64
|
||||
filter int16
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
_SA_RESTART = 0x2
|
||||
_SA_ONSTACK = 0x1
|
||||
|
||||
_CLOCK_MONOTONIC = 0x4
|
||||
|
||||
_UMTX_OP_WAIT_UINT = 0xb
|
||||
_UMTX_OP_WAIT_UINT_PRIVATE = 0xf
|
||||
_UMTX_OP_WAKE = 0x3
|
||||
@@ -176,6 +178,12 @@ type itimerval struct {
|
||||
it_value timeval
|
||||
}
|
||||
|
||||
type umtx_time struct {
|
||||
_timeout timespec
|
||||
_flags uint32
|
||||
_clockid uint32
|
||||
}
|
||||
|
||||
type keventt struct {
|
||||
ident uint32
|
||||
filter int16
|
||||
|
||||
@@ -917,7 +917,9 @@ func scavengelist(list *mSpanList, now, limit uint64) uintptr {
|
||||
// more memory than we want.)
|
||||
start = (start + sys.PhysPageSize - 1) &^ (sys.PhysPageSize - 1)
|
||||
end &^= sys.PhysPageSize - 1
|
||||
if start == end {
|
||||
if end <= start {
|
||||
// start and end don't span a
|
||||
// whole physical page.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func raise(sig int32)
|
||||
func raiseproc(sig int32)
|
||||
|
||||
//go:noescape
|
||||
func sys_umtx_op(addr *uint32, mode int32, val uint32, ptr2, ts *timespec) int32
|
||||
func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uintptr, ut *umtx_time) int32
|
||||
|
||||
func osyield()
|
||||
|
||||
@@ -70,14 +70,14 @@ func futexsleep(addr *uint32, val uint32, ns int64) {
|
||||
}
|
||||
|
||||
func futexsleep1(addr *uint32, val uint32, ns int64) {
|
||||
var tsp *timespec
|
||||
var utp *umtx_time
|
||||
if ns >= 0 {
|
||||
var ts timespec
|
||||
ts.tv_nsec = 0
|
||||
ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec)))))
|
||||
tsp = &ts
|
||||
var ut umtx_time
|
||||
ut._clockid = _CLOCK_MONOTONIC
|
||||
ut._timeout.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ut._timeout.tv_nsec)))))
|
||||
utp = &ut
|
||||
}
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, nil, tsp)
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp)
|
||||
if ret >= 0 || ret == -_EINTR {
|
||||
return
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func futexsleep1(addr *uint32, val uint32, ns int64) {
|
||||
|
||||
//go:nosplit
|
||||
func futexwakeup(addr *uint32, cnt uint32) {
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, nil, nil)
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, 0, nil)
|
||||
if ret >= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
MOVQ addr+0(FP), DI
|
||||
MOVL mode+8(FP), SI
|
||||
MOVL val+12(FP), DX
|
||||
MOVQ ptr2+16(FP), R10
|
||||
MOVQ ts+24(FP), R8
|
||||
MOVQ uaddr1+16(FP), R10
|
||||
MOVQ ut+24(FP), R8
|
||||
MOVL $454, AX
|
||||
SYSCALL
|
||||
MOVL AX, ret+32(FP)
|
||||
|
||||
@@ -45,7 +45,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
MOVW addr+0(FP), R0
|
||||
MOVW mode+4(FP), R1
|
||||
MOVW val+8(FP), R2
|
||||
MOVW ptr2+12(FP), R3
|
||||
MOVW uaddr1+12(FP), R3
|
||||
ADD $20, R13 // arg 5 is passed on stack
|
||||
MOVW $SYS__umtx_op, R7
|
||||
SWI $0
|
||||
|
||||
@@ -7,28 +7,45 @@
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const MNT_WAIT = 1
|
||||
const MNT_NOWAIT = 2
|
||||
|
||||
func TestGetfsstat(t *testing.T) {
|
||||
n, err := syscall.Getfsstat(nil, MNT_WAIT)
|
||||
const flags = MNT_NOWAIT // see Issue 16937
|
||||
n, err := syscall.Getfsstat(nil, flags)
|
||||
t.Logf("Getfsstat(nil, %d) = (%v, %v)", flags, n, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data := make([]syscall.Statfs_t, n)
|
||||
n, err = syscall.Getfsstat(data, MNT_WAIT)
|
||||
n2, err := syscall.Getfsstat(data, flags)
|
||||
t.Logf("Getfsstat([]syscall.Statfs_t, %d) = (%v, %v)", flags, n2, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
empty := syscall.Statfs_t{}
|
||||
for _, stat := range data {
|
||||
if stat == empty {
|
||||
t.Fatal("an empty Statfs_t struct was returned")
|
||||
if n != n2 {
|
||||
t.Errorf("Getfsstat(nil) = %d, but subsequent Getfsstat(slice) = %d", n, n2)
|
||||
}
|
||||
for i, stat := range data {
|
||||
if stat == (syscall.Statfs_t{}) {
|
||||
t.Errorf("index %v is an empty Statfs_t struct", i)
|
||||
}
|
||||
}
|
||||
if t.Failed() {
|
||||
for i, stat := range data[:n2] {
|
||||
t.Logf("data[%v] = %+v", i, stat)
|
||||
}
|
||||
mount, err := exec.Command("mount").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("mount: %v\n%s", err, mount)
|
||||
} else {
|
||||
t.Logf("mount: %s", mount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixedbugs/issue17318.go
Normal file
47
test/fixedbugs/issue17318.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// errorcheck -0 -N -m -l
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The escape analyzer needs to run till its root set settles
|
||||
// (this is not that often, it turns out).
|
||||
// This test is likely to become stale because the leak depends
|
||||
// on a spurious-escape bug -- return an interface as a named
|
||||
// output parameter appears to cause the called closure to escape,
|
||||
// where returning it as a regular type does not.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type closure func(i, j int) ent
|
||||
|
||||
type ent int
|
||||
|
||||
func (e ent) String() string {
|
||||
return fmt.Sprintf("%d", int(e)) // ERROR "ent.String ... argument does not escape$" "int\(e\) escapes to heap$"
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "leaking param: ops$" "leaking param: ops to result err level=0$"
|
||||
enqueue := func(i int) fmt.Stringer { // ERROR "func literal escapes to heap$"
|
||||
return ops(i, j) // ERROR "ops\(i, j\) escapes to heap$"
|
||||
}
|
||||
err = enqueue(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return // return result of enqueue, a fmt.Stringer
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 3 identical functions, to get different escape behavior.
|
||||
f := func(i, j int) ent { // ERROR "func literal escapes to heap$"
|
||||
return ent(i + j)
|
||||
}
|
||||
i := foo(f, 3).(ent)
|
||||
fmt.Printf("foo(f,3)=%d\n", int(i)) // ERROR "int\(i\) escapes to heap$" "main ... argument does not escape$"
|
||||
}
|
||||
Reference in New Issue
Block a user