[dev.link] all: merge branch 'master' into dev.link

Change-Id: I85b653b621ad8cb2ef27886210ea2c4b7409b60d
This commit is contained in:
Cherry Zhang 2020-05-21 14:08:32 -04:00
commit 6097f7cf7a
45 changed files with 485 additions and 151 deletions

View file

@ -33,6 +33,12 @@ TODO
<h3 id="darwin">Darwin</h3> <h3 id="darwin">Darwin</h3>
<p>
As <a href="go1.14#darwin">announced</a> in the Go 1.14 release notes,
Go 1.15 now requires macOS 10.12 Sierra or later;
support for previous versions has been discontinued.
</p>
<p> <!-- golang.org/issue/37610, golang.org/issue/37611 --> <p> <!-- golang.org/issue/37610, golang.org/issue/37611 -->
As <a href="/doc/go1.14#darwin">announced</a> in the Go 1.14 release As <a href="/doc/go1.14#darwin">announced</a> in the Go 1.14 release
notes, Go 1.15 drops support for 32-bit binaries on macOS, iOS, notes, Go 1.15 drops support for 32-bit binaries on macOS, iOS,
@ -116,6 +122,73 @@ TODO
<code>GODEBUG=modcacheunzipinplace=1</code>. <code>GODEBUG=modcacheunzipinplace=1</code>.
</p> </p>
<h3 id="vet">Vet</h3>
<h4 id="vet-string-int">New warning for string(x)</h4>
<p><!-- CL 212919, 232660 -->
The vet tool now warns about conversions of the
form <code>string(x)</code> where <code>x</code> has an integer type
other than <code>rune</code> or <code>byte</code>.
Experience with Go has shown that many conversions of this form
erroneously assume that <code>string(x)</code> evaluates to the
string representation of the integer <code>x</code>.
It actually evaluates to a string containing the UTF-8 encoding of
the value of <code>x</code>.
For example, <code>string(9786)</code> does not evaluate to the
string <code>"9786"</code>; it evaluates to the
string <code>"\xe2\x98\xba"</code>, or <code>"☺"</code>.
</p>
<p>
Code that is using <code>string(x)</code> correctly can be rewritten
to <code>string(rune(x))</code>.
Or, in some cases, calling <code>utf8.EncodeRune(buf, x)</code> with
a suitable byte slice <code>buf</code> may be the right solution.
Other code should most likely use <code>strconv.Itoa</code>
or <code>fmt.Sprint</code>.
</p>
<p>
This new vet check is enabled by default when using <code>go test</code>.
</p>
<p>
We are considering prohibiting the conversion in a future release of Go.
That is, the language would change to only
permit <code>string(x)</code> for integer <code>x</code> when the
type of <code>x</code> is <code>rune</code> or <code>byte</code>.
Such a language change would not be backward compatible.
We are using this vet check as a first trial step toward changing
the language.
</p>
<h4 id="vet-impossible-interface">New warning for impossible interface conversions</h4>
<p><!-- CL 218779, 232660 -->
The vet tool now warns about type assertions from one interface type
to another interface type when the type assertion will always fail.
This will happen if both interface types implement a method with the
same name but with a different type signature.
</p>
<p>
There is no reason to write a type assertion that always fails, so
any code that triggers this vet check should be rewritten.
</p>
<p>
This new vet check is enabled by default when using <code>go test</code>.
</p>
<p>
We are considering prohibiting impossible interface type assertions
in a future release of Go.
Such a language change would not be backward compatible.
We are using this vet check as a first trial step toward changing
the language.
</p>
<h2 id="runtime">Runtime</h2> <h2 id="runtime">Runtime</h2>
<p> <p>
@ -155,22 +228,6 @@ TODO
TODO TODO
</p> </p>
<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
<dd>
<p><!-- golang.org/issue/28135 -->
The <code>testing.T</code> type now has a <code>Deadline</code> method
that reports the time at which the test binary will have exceeded its
timeout.
</p>
<p><!-- golang.org/issue/34129 -->
A <code>TestMain</code> function is no longer required to call
<code>os.Exit</code>. If a <code>TestMain</code> function returns,
the test binary will call <code>os.Exit</code> with the value returned
by <code>m.Run</code>.
</p>
</dd>
</dl><!-- testing -->
<h3 id="minor_library_changes">Minor changes to the library</h3> <h3 id="minor_library_changes">Minor changes to the library</h3>
<p> <p>
@ -375,6 +432,20 @@ TODO
<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt> <dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
<dd> <dd>
<p><!-- golang.org/issue/28135 -->
The <code>testing.T</code> type now has a
<a href="/pkg/testing/#T.Deadline"><code>Deadline</code></a> method
that reports the time at which the test binary will have exceeded its
timeout.
</p>
<p><!-- golang.org/issue/34129 -->
A <code>TestMain</code> function is no longer required to call
<code>os.Exit</code>. If a <code>TestMain</code> function returns,
the test binary will call <code>os.Exit</code> with the value returned
by <code>m.Run</code>.
</p>
<p><!-- CL 226877, golang.org/issue/35998 --> <p><!-- CL 226877, golang.org/issue/35998 -->
The new methods The new methods
<a href="/pkg/testing/#T.TempDir"><code>T.TempDir</code></a> and <a href="/pkg/testing/#T.TempDir"><code>T.TempDir</code></a> and

View file

@ -501,7 +501,7 @@ These default to the values of <code>$GOHOSTOS</code> and
<p> <p>
Choices for <code>$GOOS</code> are Choices for <code>$GOOS</code> are
<code>android</code>, <code>darwin</code> (macOS 10.11 and above and iOS), <code>android</code>, <code>darwin</code> (macOS/iOS),
<code>dragonfly</code>, <code>freebsd</code>, <code>illumos</code>, <code>js</code>, <code>dragonfly</code>, <code>freebsd</code>, <code>illumos</code>, <code>js</code>,
<code>linux</code>, <code>netbsd</code>, <code>openbsd</code>, <code>linux</code>, <code>netbsd</code>, <code>openbsd</code>,
<code>plan9</code>, <code>solaris</code> and <code>windows</code>. <code>plan9</code>, <code>solaris</code> and <code>windows</code>.

View file

@ -271,7 +271,7 @@ func compile(fn *Node) {
} }
} }
if compilenow() { if compilenow(fn) {
compileSSA(fn, 0) compileSSA(fn, 0)
} else { } else {
compilequeue = append(compilequeue, fn) compilequeue = append(compilequeue, fn)
@ -282,10 +282,31 @@ func compile(fn *Node) {
// If functions are not compiled immediately, // If functions are not compiled immediately,
// they are enqueued in compilequeue, // they are enqueued in compilequeue,
// which is drained by compileFunctions. // which is drained by compileFunctions.
func compilenow() bool { func compilenow(fn *Node) bool {
// Issue 38068: if this function is a method AND an inline
// candidate AND was not inlined (yet), put it onto the compile
// queue instead of compiling it immediately. This is in case we
// wind up inlining it into a method wrapper that is generated by
// compiling a function later on in the xtop list.
if fn.IsMethod() && isInlinableButNotInlined(fn) {
return false
}
return nBackendWorkers == 1 && Debug_compilelater == 0 return nBackendWorkers == 1 && Debug_compilelater == 0
} }
// isInlinableButNotInlined returns true if 'fn' was marked as an
// inline candidate but then never inlined (presumably because we
// found no call sites).
func isInlinableButNotInlined(fn *Node) bool {
if fn.Func.Nname.Func.Inl == nil {
return false
}
if fn.Sym == nil {
return true
}
return !fn.Sym.Linksym().WasInlined()
}
const maxStackSize = 1 << 30 const maxStackSize = 1 << 30
// compileSSA builds an SSA backend function, // compileSSA builds an SSA backend function,

11
src/cmd/dist/build.go vendored
View file

@ -31,7 +31,6 @@ var (
goos string goos string
goarm string goarm string
go386 string go386 string
goamd64 string
gomips string gomips string
gomips64 string gomips64 string
goppc64 string goppc64 string
@ -152,12 +151,6 @@ func xinit() {
} }
go386 = b go386 = b
b = os.Getenv("GOAMD64")
if b == "" {
b = "alignedjumps"
}
goamd64 = b
b = os.Getenv("GOMIPS") b = os.Getenv("GOMIPS")
if b == "" { if b == "" {
b = "hardfloat" b = "hardfloat"
@ -230,7 +223,6 @@ func xinit() {
// For tools being invoked but also for os.ExpandEnv. // For tools being invoked but also for os.ExpandEnv.
os.Setenv("GO386", go386) os.Setenv("GO386", go386)
os.Setenv("GOAMD64", goamd64)
os.Setenv("GOARCH", goarch) os.Setenv("GOARCH", goarch)
os.Setenv("GOARM", goarm) os.Setenv("GOARM", goarm)
os.Setenv("GOHOSTARCH", gohostarch) os.Setenv("GOHOSTARCH", gohostarch)
@ -1171,9 +1163,6 @@ func cmdenv() {
if goarch == "386" { if goarch == "386" {
xprintf(format, "GO386", go386) xprintf(format, "GO386", go386)
} }
if goarch == "amd64" {
xprintf(format, "GOAMD64", goamd64)
}
if goarch == "mips" || goarch == "mipsle" { if goarch == "mips" || goarch == "mipsle" {
xprintf(format, "GOMIPS", gomips) xprintf(format, "GOMIPS", gomips)
} }

View file

@ -42,7 +42,6 @@ func mkzversion(dir, file string) {
// //
// const defaultGOROOT = <goroot> // const defaultGOROOT = <goroot>
// const defaultGO386 = <go386> // const defaultGO386 = <go386>
// const defaultGOAMD64 = <goamd64>
// const defaultGOARM = <goarm> // const defaultGOARM = <goarm>
// const defaultGOMIPS = <gomips> // const defaultGOMIPS = <gomips>
// const defaultGOMIPS64 = <gomips64> // const defaultGOMIPS64 = <gomips64>
@ -72,7 +71,6 @@ func mkzbootstrap(file string) {
fmt.Fprintf(&buf, "import \"runtime\"\n") fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386) fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm) fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips) fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64) fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)

View file

@ -7,7 +7,7 @@ require (
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d golang.org/x/arch v0.0.0-20200511175325-f7c78586839d
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2 golang.org/x/mod v0.3.0
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
golang.org/x/tools v0.0.0-20200504152539-33427f1b0364 golang.org/x/tools v0.0.0-20200504152539-33427f1b0364
) )

View file

@ -14,8 +14,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2 h1:VUsRDZIYpMs3R7PyYeN7BSbDfYjhxaX6HlWvM5iAEqs= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=

View file

@ -1754,9 +1754,6 @@
// GO386 // GO386
// For GOARCH=386, the floating point instruction set. // For GOARCH=386, the floating point instruction set.
// Valid values are 387, sse2. // Valid values are 387, sse2.
// GOAMD64
// For GOARCH=amd64, jumps can be optionally be aligned such that they do not end on
// or cross 32 byte boundaries. Valid values are alignedjumps (default), normaljumps.
// GOMIPS // GOMIPS
// For GOARCH=mips{,le}, whether to use floating point instructions. // For GOARCH=mips{,le}, whether to use floating point instructions.
// Valid values are hardfloat (default), softfloat. // Valid values are hardfloat (default), softfloat.

View file

@ -241,7 +241,6 @@ var (
// Used in envcmd.MkEnv and build ID computations. // Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM)) GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
GO386 = envOr("GO386", objabi.GO386) GO386 = envOr("GO386", objabi.GO386)
GOAMD64 = envOr("GOAMD64", objabi.GOAMD64)
GOMIPS = envOr("GOMIPS", objabi.GOMIPS) GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64) GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64)) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
@ -267,8 +266,6 @@ func GetArchEnv() (key, val string) {
return "GOARM", GOARM return "GOARM", GOARM
case "386": case "386":
return "GO386", GO386 return "GO386", GO386
case "amd64":
return "GOAMD64", GOAMD64
case "mips", "mipsle": case "mips", "mipsle":
return "GOMIPS", GOMIPS return "GOMIPS", GOMIPS
case "mips64", "mips64le": case "mips64", "mips64le":

View file

@ -582,9 +582,6 @@ Architecture-specific environment variables:
GO386 GO386
For GOARCH=386, the floating point instruction set. For GOARCH=386, the floating point instruction set.
Valid values are 387, sse2. Valid values are 387, sse2.
GOAMD64
For GOARCH=amd64, jumps can be optionally be aligned such that they do not end on
or cross 32 byte boundaries. Valid values are alignedjumps (default), normaljumps.
GOMIPS GOMIPS
For GOARCH=mips{,le}, whether to use floating point instructions. For GOARCH=mips{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat. Valid values are hardfloat (default), softfloat.

View file

@ -196,8 +196,12 @@ func TryProxies(f func(proxy string) error) error {
// We try to report the most helpful error to the user. "direct" and "noproxy" // We try to report the most helpful error to the user. "direct" and "noproxy"
// errors are best, followed by proxy errors other than ErrNotExist, followed // errors are best, followed by proxy errors other than ErrNotExist, followed
// by ErrNotExist. Note that errProxyOff, errNoproxy, and errUseProxy are // by ErrNotExist.
// equivalent to ErrNotExist. //
// Note that errProxyOff, errNoproxy, and errUseProxy are equivalent to
// ErrNotExist. errUseProxy should only be returned if "noproxy" is the only
// proxy. errNoproxy should never be returned, since there should always be a
// more useful error from "noproxy" first.
const ( const (
notExistRank = iota notExistRank = iota
proxyRank proxyRank
@ -212,7 +216,7 @@ func TryProxies(f func(proxy string) error) error {
} }
isNotExistErr := errors.Is(err, os.ErrNotExist) isNotExistErr := errors.Is(err, os.ErrNotExist)
if proxy.url == "direct" || proxy.url == "noproxy" { if proxy.url == "direct" || (proxy.url == "noproxy" && err != errUseProxy) {
bestErr = err bestErr = err
bestErrRank = directRank bestErrRank = directRank
} else if bestErrRank <= proxyRank && !isNotExistErr { } else if bestErrRank <= proxyRank && !isNotExistErr {

View file

@ -2434,13 +2434,25 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
if b.flagCache == nil { if b.flagCache == nil {
b.flagCache = make(map[[2]string]bool) b.flagCache = make(map[[2]string]bool)
} }
tmp := os.DevNull
if runtime.GOOS == "windows" {
f, err := ioutil.TempFile(b.WorkDir, "")
if err != nil {
return false
}
f.Close()
tmp = f.Name()
defer os.Remove(tmp)
}
// We used to write an empty C file, but that gets complicated with // We used to write an empty C file, but that gets complicated with
// go build -n. We tried using a file that does not exist, but that // go build -n. We tried using a file that does not exist, but that
// fails on systems with GCC version 4.2.1; that is the last GPLv2 // fails on systems with GCC version 4.2.1; that is the last GPLv2
// version of GCC, so some systems have frozen on it. // version of GCC, so some systems have frozen on it.
// Now we pass an empty file on stdin, which should work at least for // Now we pass an empty file on stdin, which should work at least for
// GCC and clang. // GCC and clang.
cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", os.DevNull) cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", tmp)
if cfg.BuildN || cfg.BuildX { if cfg.BuildN || cfg.BuildX {
b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs)) b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
if cfg.BuildN { if cfg.BuildN {

View file

@ -53,7 +53,7 @@ func TestNoteReading(t *testing.T) {
// we've had trouble reading the notes generated by gold. // we've had trouble reading the notes generated by gold.
err := tg.doRun([]string{"build", "-ldflags", "-buildid=" + buildID + " -linkmode=external -extldflags=-fuse-ld=gold", "-o", tg.path("hello3.exe"), tg.path("hello.go")}) err := tg.doRun([]string{"build", "-ldflags", "-buildid=" + buildID + " -linkmode=external -extldflags=-fuse-ld=gold", "-o", tg.path("hello3.exe"), tg.path("hello.go")})
if err != nil { if err != nil {
if tg.grepCountBoth("(invalid linker|gold|cannot find 'ld')") > 0 { if tg.grepCountBoth("(invalid linker|gold|cannot find [']ld['])") > 0 {
// It's not an error if gold isn't there. gcc claims it "cannot find 'ld'" if // It's not an error if gold isn't there. gcc claims it "cannot find 'ld'" if
// ld.gold is missing, see issue #22340. // ld.gold is missing, see issue #22340.
t.Log("skipping gold test") t.Log("skipping gold test")

View file

@ -0,0 +1,6 @@
# Tests golang.org/issue/36000
[!cgo] skip
# go env with CGO flags should not make NUL file
go env CGO_CFLAGS

View file

@ -10,7 +10,7 @@ env GOSUMDB=$sumdb' '$proxy/sumdb-wrong
! go get rsc.io/quote ! go get rsc.io/quote
stderr 'SECURITY ERROR' stderr 'SECURITY ERROR'
# but GONOSUMDB bypasses sumdb, for rsc.io/quote, rsc.io/sampler, golang.org/x/text # GONOSUMDB bypasses sumdb, for rsc.io/quote, rsc.io/sampler, golang.org/x/text
env GONOSUMDB='*/quote,*/*mple*,golang.org/x' env GONOSUMDB='*/quote,*/*mple*,golang.org/x'
go get rsc.io/quote go get rsc.io/quote
rm go.sum rm go.sum
@ -18,7 +18,13 @@ env GOPRIVATE='*/quote,*/*mple*,golang.org/x'
env GONOPROXY=none # that is, proxy all despite GOPRIVATE env GONOPROXY=none # that is, proxy all despite GOPRIVATE
go get rsc.io/quote go get rsc.io/quote
# and GONOPROXY bypasses proxy # When GOPROXY=off, fetching modules not matched by GONOPROXY fails.
env GONOPROXY=*/fortune
env GOPROXY=off
! go get golang.org/x/text
stderr '^go get golang.org/x/text: module lookup disabled by GOPROXY=off$'
# GONOPROXY bypasses proxy
[!net] skip [!net] skip
[!exec:git] skip [!exec:git] skip
env GOPRIVATE=none env GOPRIVATE=none

View file

@ -425,6 +425,14 @@ func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) {
absfn.Type = objabi.SDWARFINFO absfn.Type = objabi.SDWARFINFO
ft.ctxt.Data = append(ft.ctxt.Data, absfn) ft.ctxt.Data = append(ft.ctxt.Data, absfn)
// In the case of "late" inlining (inlines that happen during
// wrapper generation as opposed to the main inlining phase) it's
// possible that we didn't cache the abstract function sym for the
// text symbol -- do so now if needed. See issue 38068.
if s.Func != nil && s.Func.dwarfAbsFnSym == nil {
s.Func.dwarfAbsFnSym = absfn
}
ft.precursor[s] = fnState{precursor: fn, absfn: absfn} ft.precursor[s] = fnState{precursor: fn, absfn: absfn}
} }

View file

@ -37,16 +37,13 @@ var (
const ( const (
ElfRelocOffset = 256 ElfRelocOffset = 256
MachoRelocOffset = 2048 // reserve enough space for ELF relocations MachoRelocOffset = 2048 // reserve enough space for ELF relocations
Go115AMD64 = "alignedjumps" // Should be "alignedjumps" or "normaljumps"; this replaces environment variable introduced in CL 219357.
) )
// TODO(1.16): assuming no issues in 1.15 release, remove this and related constant.
func goamd64() string { func goamd64() string {
switch v := envOr("GOAMD64", defaultGOAMD64); v { return Go115AMD64
case "normaljumps", "alignedjumps":
return v
}
log.Fatalf("Invalid GOAMD64 value. Must be normaljumps or alignedjumps.")
panic("unreachable")
} }
func goarm() int { func goarm() int {

View file

@ -385,12 +385,16 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
offset := (signext24(r.Add()&0xffffff) + 2) * 4 offset := (signext24(r.Add()&0xffffff) + 2) * 4
var tramp loader.Sym var tramp loader.Sym
for i := 0; ; i++ { for i := 0; ; i++ {
name := ldr.SymName(rs) + fmt.Sprintf("%+d-tramp%d", offset, i) oName := ldr.SymName(rs)
name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
if ldr.SymType(tramp) == sym.SDYNIMPORT { if ldr.SymType(tramp) == sym.SDYNIMPORT {
// don't reuse trampoline defined in other module // don't reuse trampoline defined in other module
continue continue
} }
if oName == "runtime.deferreturn" {
ldr.SetIsDeferReturnTramp(tramp, true)
}
if ldr.SymValue(tramp) == 0 { if ldr.SymValue(tramp) == 0 {
// either the trampoline does not exist -- we need to create one, // either the trampoline does not exist -- we need to create one,
// or found one the address which is not assigned -- this will be // or found one the address which is not assigned -- this will be

View file

@ -0,0 +1,50 @@
// Copyright 2020 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.
// +build darwin linux
package ld
import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
)
func TestFallocate(t *testing.T) {
dir, err := ioutil.TempDir("", "TestFallocate")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
filename := filepath.Join(dir, "a.out")
out := NewOutBuf(nil)
err = out.Open(filename)
if err != nil {
t.Fatalf("Open file failed: %v", err)
}
defer out.Close()
// Mmap 1 MiB initially, and grow to 2 and 3 MiB.
// Check if the file size and disk usage is expected.
for _, sz := range []int64{1 << 20, 2 << 20, 3 << 20} {
err = out.Mmap(uint64(sz))
if err != nil {
t.Fatalf("Mmap failed: %v", err)
}
stat, err := os.Stat(filename)
if err != nil {
t.Fatalf("Stat failed: %v", err)
}
if got := stat.Size(); got != sz {
t.Errorf("unexpected file size: got %d, want %d", got, sz)
}
if got, want := stat.Sys().(*syscall.Stat_t).Blocks, (sz+511)/512; got != want {
t.Errorf("unexpected disk usage: got %d blocks, want %d", got, want)
}
out.munmap()
}
}

View file

@ -10,16 +10,25 @@ import (
) )
func (out *OutBuf) fallocate(size uint64) error { func (out *OutBuf) fallocate(size uint64) error {
stat, err := out.f.Stat()
if err != nil {
return err
}
cursize := uint64(stat.Size())
if size <= cursize {
return nil
}
store := &syscall.Fstore_t{ store := &syscall.Fstore_t{
Flags: syscall.F_ALLOCATEALL, Flags: syscall.F_ALLOCATEALL,
Posmode: syscall.F_PEOFPOSMODE, Posmode: syscall.F_PEOFPOSMODE,
Offset: 0, Offset: 0,
Length: int64(size), Length: int64(size - cursize), // F_PEOFPOSMODE allocates from the end of the file, so we want the size difference here
} }
_, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store))) _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store)))
if err != 0 { if errno != 0 {
return err return errno
} }
return nil return nil

View file

@ -161,7 +161,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32
// set the resumption point to PC_B. // set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add()) lastWasmAddr = uint32(r.Add())
} }
if r.Type().IsDirectCall() && r.Sym() == state.deferReturnSym { if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
if target.IsWasm() { if target.IsWasm() {
deferreturn = lastWasmAddr - 1 deferreturn = lastWasmAddr - 1
} else { } else {

View file

@ -229,7 +229,8 @@ type Loader struct {
outdata [][]byte // symbol's data in the output buffer outdata [][]byte // symbol's data in the output buffer
extRelocs [][]ExtReloc // symbol's external relocations extRelocs [][]ExtReloc // symbol's external relocations
itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.* itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
objByPkg map[string]*oReader // map package path to its Go object reader objByPkg map[string]*oReader // map package path to its Go object reader
@ -354,6 +355,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
attrCgoExportDynamic: make(map[Sym]struct{}), attrCgoExportDynamic: make(map[Sym]struct{}),
attrCgoExportStatic: make(map[Sym]struct{}), attrCgoExportStatic: make(map[Sym]struct{}),
itablink: make(map[Sym]struct{}), itablink: make(map[Sym]struct{}),
deferReturnTramp: make(map[Sym]bool),
extStaticSyms: make(map[nameVer]Sym), extStaticSyms: make(map[nameVer]Sym),
builtinSyms: make([]Sym, nbuiltin), builtinSyms: make([]Sym, nbuiltin),
flags: flags, flags: flags,
@ -1050,6 +1052,16 @@ func (l *Loader) IsItabLink(i Sym) bool {
return false return false
} }
// Return whether this is a trampoline of a deferreturn call.
func (l *Loader) IsDeferReturnTramp(i Sym) bool {
return l.deferReturnTramp[i]
}
// Set that i is a trampoline of a deferreturn call.
func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
l.deferReturnTramp[i] = v
}
// growValues grows the slice used to store symbol values. // growValues grows the slice used to store symbol values.
func (l *Loader) growValues(reqLen int) { func (l *Loader) growValues(reqLen int) {
curLen := len(l.values) curLen := len(l.values)

View file

@ -686,16 +686,20 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
// target is at some offset within the function. Calls to duff+8 and duff+256 must appear as // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
// distinct trampolines. // distinct trampolines.
name := ldr.SymName(rs) oName := ldr.SymName(rs)
name := oName
if r.Add() == 0 { if r.Add() == 0 {
name = name + fmt.Sprintf("-tramp%d", i) name += fmt.Sprintf("-tramp%d", i)
} else { } else {
name = name + fmt.Sprintf("%+x-tramp%d", r.Add(), i) name += fmt.Sprintf("%+x-tramp%d", r.Add(), i)
} }
// Look up the trampoline in case it already exists // Look up the trampoline in case it already exists
tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
if oName == "runtime.deferreturn" {
ldr.SetIsDeferReturnTramp(tramp, true)
}
if ldr.SymValue(tramp) == 0 { if ldr.SymValue(tramp) == 0 {
break break
} }

View file

@ -605,10 +605,23 @@ func TestFuncAlign(t *testing.T) {
} }
} }
const helloSrc = ` const testTrampSrc = `
package main package main
import "fmt" import "fmt"
func main() { fmt.Println("hello") } func main() {
fmt.Println("hello")
defer func(){
if e := recover(); e == nil {
panic("did not panic")
}
}()
f1()
}
// Test deferreturn trampolines. See issue #39049.
func f1() { defer f2() }
func f2() { panic("XXX") }
` `
func TestTrampoline(t *testing.T) { func TestTrampoline(t *testing.T) {
@ -631,7 +644,7 @@ func TestTrampoline(t *testing.T) {
defer os.RemoveAll(tmpdir) defer os.RemoveAll(tmpdir)
src := filepath.Join(tmpdir, "hello.go") src := filepath.Join(tmpdir, "hello.go")
err = ioutil.WriteFile(src, []byte(helloSrc), 0666) err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -29,7 +29,7 @@ golang.org/x/arch/x86/x86asm
golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519
golang.org/x/crypto/ed25519/internal/edwards25519 golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/ssh/terminal golang.org/x/crypto/ssh/terminal
# golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2 # golang.org/x/mod v0.3.0
## explicit ## explicit
golang.org/x/mod/internal/lazyregexp golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile golang.org/x/mod/modfile

View file

@ -980,7 +980,28 @@ func testResumption(t *testing.T, version uint16) {
if bytes.Equal(ticket, getTicket()) { if bytes.Equal(ticket, getTicket()) {
t.Fatal("new ticket wasn't provided after old ticket expired") t.Fatal("new ticket wasn't provided after old ticket expired")
} }
testResumeState("FreshSessionTicket", true)
// Age the session ticket a bit at a time, but don't expire it.
d := 0 * time.Hour
for i := 0; i < 13; i++ {
d += 12 * time.Hour
serverConfig.Time = func() time.Time { return time.Now().Add(d) }
testResumeState("OldSessionTicket", true)
}
// Expire it (now a little more than 7 days) and make sure a full
// handshake occurs for TLS 1.2. Resumption should still occur for
// TLS 1.3 since the client should be using a fresh ticket sent over
// by the server.
d += 12 * time.Hour
serverConfig.Time = func() time.Time { return time.Now().Add(d) }
if version == VersionTLS13 {
testResumeState("ExpiredSessionTicket", true)
} else {
testResumeState("ExpiredSessionTicket", false)
}
if bytes.Equal(ticket, getTicket()) {
t.Fatal("new ticket wasn't provided after old ticket expired")
}
// Reset serverConfig to ensure that calling SetSessionTicketKeys // Reset serverConfig to ensure that calling SetSessionTicketKeys
// before the serverConfig is used works. // before the serverConfig is used works.

View file

@ -75,13 +75,8 @@ func (hs *serverHandshakeState) handshake() error {
if err := hs.establishKeys(); err != nil { if err := hs.establishKeys(); err != nil {
return err return err
} }
// ticketSupported is set in a resumption handshake if the if err := hs.sendSessionTicket(); err != nil {
// ticket from the client was encrypted with an old session return err
// ticket key and thus a refreshed ticket should be sent.
if hs.hello.ticketSupported {
if err := hs.sendSessionTicket(); err != nil {
return err
}
} }
if err := hs.sendFinished(c.serverFinished[:]); err != nil { if err := hs.sendFinished(c.serverFinished[:]); err != nil {
return err return err
@ -688,6 +683,9 @@ func (hs *serverHandshakeState) readFinished(out []byte) error {
} }
func (hs *serverHandshakeState) sendSessionTicket() error { func (hs *serverHandshakeState) sendSessionTicket() error {
// ticketSupported is set in a resumption handshake if the
// ticket from the client was encrypted with an old session
// ticket key and thus a refreshed ticket should be sent.
if !hs.hello.ticketSupported { if !hs.hello.ticketSupported {
return nil return nil
} }
@ -695,6 +693,13 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
c := hs.c c := hs.c
m := new(newSessionTicketMsg) m := new(newSessionTicketMsg)
createdAt := uint64(c.config.time().Unix())
if hs.sessionState != nil {
// If this is re-wrapping an old key, then keep
// the original time it was created.
createdAt = hs.sessionState.createdAt
}
var certsFromClient [][]byte var certsFromClient [][]byte
for _, cert := range c.peerCertificates { for _, cert := range c.peerCertificates {
certsFromClient = append(certsFromClient, cert.Raw) certsFromClient = append(certsFromClient, cert.Raw)
@ -702,7 +707,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
state := sessionState{ state := sessionState{
vers: c.vers, vers: c.vers,
cipherSuite: hs.suite.id, cipherSuite: hs.suite.id,
createdAt: uint64(c.config.time().Unix()), createdAt: createdAt,
masterSecret: hs.masterSecret, masterSecret: hs.masterSecret,
certificates: certsFromClient, certificates: certsFromClient,
} }

View file

@ -54,7 +54,6 @@ func (m *sessionState) unmarshal(data []byte) bool {
*m = sessionState{usedOldKey: m.usedOldKey} *m = sessionState{usedOldKey: m.usedOldKey}
s := cryptobyte.String(data) s := cryptobyte.String(data)
if ok := s.ReadUint16(&m.vers) && if ok := s.ReadUint16(&m.vers) &&
m.vers != VersionTLS13 &&
s.ReadUint16(&m.cipherSuite) && s.ReadUint16(&m.cipherSuite) &&
readUint64(&s, &m.createdAt) && readUint64(&s, &m.createdAt) &&
readUint16LengthPrefixed(&s, &m.masterSecret) && readUint16LengthPrefixed(&s, &m.masterSecret) &&

View file

@ -204,7 +204,8 @@ func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
} }
func TestReadUniqueDirectoryEntries(t *testing.T) { func TestReadUniqueDirectoryEntries(t *testing.T) {
temp := func(base string) string { return filepath.Join(t.TempDir(), base) } tmp := t.TempDir()
temp := func(base string) string { return filepath.Join(tmp, base) }
if f, err := os.Create(temp("file")); err != nil { if f, err := os.Create(temp("file")); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
@ -216,7 +217,7 @@ func TestReadUniqueDirectoryEntries(t *testing.T) {
if err := os.Symlink("../target-out", temp("link-out")); err != nil { if err := os.Symlink("../target-out", temp("link-out")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
got, err := readUniqueDirectoryEntries(t.TempDir()) got, err := readUniqueDirectoryEntries(tmp)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -33,7 +33,6 @@ const KnownEnv = `
GCCGO GCCGO
GO111MODULE GO111MODULE
GO386 GO386
GOAMD64
GOARCH GOARCH
GOARM GOARM
GOBIN GOBIN

View file

@ -20,8 +20,9 @@ func TestLinkerGC(t *testing.T) {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
t.Parallel() t.Parallel()
tmp := t.TempDir()
goBin := testenv.GoToolPath(t) goBin := testenv.GoToolPath(t)
goFile := filepath.Join(t.TempDir(), "x.go") goFile := filepath.Join(tmp, "x.go")
file := []byte(`package main file := []byte(`package main
import _ "math/big" import _ "math/big"
func main() {} func main() {}
@ -30,13 +31,13 @@ func main() {}
t.Fatal(err) t.Fatal(err)
} }
cmd := exec.Command(goBin, "build", "-o", "x.exe", "x.go") cmd := exec.Command(goBin, "build", "-o", "x.exe", "x.go")
cmd.Dir = t.TempDir() cmd.Dir = tmp
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("compile: %v, %s", err, out) t.Fatalf("compile: %v, %s", err, out)
} }
cmd = exec.Command(goBin, "tool", "nm", "x.exe") cmd = exec.Command(goBin, "tool", "nm", "x.exe")
cmd.Dir = t.TempDir() cmd.Dir = tmp
nm, err := cmd.CombinedOutput() nm, err := cmd.CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("nm: %v, %s", err, nm) t.Fatalf("nm: %v, %s", err, nm)

View file

@ -2539,3 +2539,34 @@ func isDeadlineExceeded(err error) bool {
} }
return true return true
} }
// Test that opening a file does not change its permissions. Issue 38225.
func TestOpenFileKeepsPermissions(t *testing.T) {
t.Parallel()
dir := t.TempDir()
name := filepath.Join(dir, "x")
f, err := Create(name)
if err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Error(err)
}
f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
if err != nil {
t.Fatal(err)
}
if fi, err := f.Stat(); err != nil {
t.Error(err)
} else if fi.Mode()&0222 == 0 {
t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
}
if err := f.Close(); err != nil {
t.Error(err)
}
if fi, err := Stat(name); err != nil {
t.Error(err)
} else if fi.Mode()&0222 == 0 {
t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
}
}

View file

@ -249,14 +249,15 @@ func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte
t.Helper() t.Helper()
hook = hookCopyFileRange(t) hook = hookCopyFileRange(t)
tmp := t.TempDir()
src, err := Create(filepath.Join(t.TempDir(), "src")) src, err := Create(filepath.Join(tmp, "src"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Cleanup(func() { src.Close() }) t.Cleanup(func() { src.Close() })
dst, err = Create(filepath.Join(t.TempDir(), "dst")) dst, err = Create(filepath.Join(tmp, "dst"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -33,6 +33,7 @@ const (
lockRankDummy lockRank = iota lockRankDummy lockRank = iota
// Locks held above sched // Locks held above sched
lockRankSysmon
lockRankScavenge lockRankScavenge
lockRankForcegc lockRankForcegc
lockRankSweepWaiters lockRankSweepWaiters
@ -113,6 +114,7 @@ const lockRankLeafRank lockRank = 1000
var lockNames = []string{ var lockNames = []string{
lockRankDummy: "", lockRankDummy: "",
lockRankSysmon: "sysmon",
lockRankScavenge: "scavenge", lockRankScavenge: "scavenge",
lockRankForcegc: "forcegc", lockRankForcegc: "forcegc",
lockRankSweepWaiters: "sweepWaiters", lockRankSweepWaiters: "sweepWaiters",
@ -194,48 +196,49 @@ func (rank lockRank) String() string {
// hchan lock can be held immediately above it when it is acquired. // hchan lock can be held immediately above it when it is acquired.
var lockPartialOrder [][]lockRank = [][]lockRank{ var lockPartialOrder [][]lockRank = [][]lockRank{
lockRankDummy: {}, lockRankDummy: {},
lockRankScavenge: {}, lockRankSysmon: {},
lockRankForcegc: {}, lockRankScavenge: {lockRankSysmon},
lockRankForcegc: {lockRankSysmon},
lockRankSweepWaiters: {}, lockRankSweepWaiters: {},
lockRankAssistQueue: {}, lockRankAssistQueue: {},
lockRankCpuprof: {}, lockRankCpuprof: {},
lockRankSweep: {}, lockRankSweep: {},
lockRankSched: {lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep}, lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep},
lockRankDeadlock: {lockRankDeadlock}, lockRankDeadlock: {lockRankDeadlock},
lockRankPanic: {lockRankDeadlock}, lockRankPanic: {lockRankDeadlock},
lockRankAllg: {lockRankSched, lockRankPanic}, lockRankAllg: {lockRankSysmon, lockRankSched, lockRankPanic},
lockRankAllp: {lockRankSched}, lockRankAllp: {lockRankSysmon, lockRankSched},
lockRankPollDesc: {}, lockRankPollDesc: {},
lockRankTimers: {lockRankScavenge, lockRankSched, lockRankAllp, lockRankPollDesc, lockRankTimers}, lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankSched, lockRankAllp, lockRankPollDesc, lockRankTimers},
lockRankItab: {}, lockRankItab: {},
lockRankReflectOffs: {lockRankItab}, lockRankReflectOffs: {lockRankItab},
lockRankHchan: {lockRankScavenge, lockRankSweep, lockRankHchan}, lockRankHchan: {lockRankScavenge, lockRankSweep, lockRankHchan},
lockRankFin: {lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan}, lockRankFin: {lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan},
lockRankNotifyList: {}, lockRankNotifyList: {},
lockRankTraceBuf: {lockRankScavenge}, lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
lockRankTraceStrings: {lockRankTraceBuf}, lockRankTraceStrings: {lockRankTraceBuf},
lockRankMspanSpecial: {lockRankScavenge, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings},
lockRankProf: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankProf: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
lockRankGcBitsArenas: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
lockRankRoot: {}, lockRankRoot: {},
lockRankTrace: {lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankSched, lockRankHchan, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankSweep}, lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankSched, lockRankHchan, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankSweep},
lockRankTraceStackTab: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankTrace}, lockRankTraceStackTab: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankTrace},
lockRankNetpollInit: {lockRankTimers}, lockRankNetpollInit: {lockRankTimers},
lockRankRwmutexW: {}, lockRankRwmutexW: {},
lockRankRwmutexR: {lockRankRwmutexW}, lockRankRwmutexR: {lockRankRwmutexW},
lockRankMcentral: {lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankMcentral: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
lockRankSpine: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankSpine: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
lockRankSpanSetSpine: {lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
lockRankGscan: {lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankNotifyList, lockRankProf, lockRankGcBitsArenas, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine}, lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankNotifyList, lockRankProf, lockRankGcBitsArenas, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine},
lockRankStackpool: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankRwmutexR, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine, lockRankGscan}, lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankRwmutexR, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine, lockRankGscan},
lockRankStackLarge: {lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankSpanSetSpine, lockRankGscan}, lockRankStackLarge: {lockRankSysmon, lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankSpanSetSpine, lockRankGscan},
lockRankDefer: {}, lockRankDefer: {},
lockRankSudog: {lockRankNotifyList, lockRankHchan}, lockRankSudog: {lockRankNotifyList, lockRankHchan},
lockRankWbufSpans: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceStrings, lockRankMspanSpecial, lockRankProf, lockRankRoot, lockRankGscan, lockRankDefer, lockRankSudog}, lockRankWbufSpans: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceStrings, lockRankMspanSpecial, lockRankProf, lockRankRoot, lockRankGscan, lockRankDefer, lockRankSudog},
lockRankMheap: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan, lockRankMspanSpecial, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankDefer, lockRankSudog, lockRankWbufSpans, lockRankSpanSetSpine}, lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan, lockRankMspanSpecial, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankDefer, lockRankSudog, lockRankWbufSpans, lockRankSpanSetSpine},
lockRankMheapSpecial: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
lockRankGlobalAlloc: {lockRankProf, lockRankSpine, lockRankSpanSetSpine, lockRankMheap, lockRankMheapSpecial}, lockRankGlobalAlloc: {lockRankProf, lockRankSpine, lockRankSpanSetSpine, lockRankMheap, lockRankMheapSpecial},
lockRankGFree: {lockRankSched}, lockRankGFree: {lockRankSched},

View file

@ -1283,9 +1283,10 @@ HaveSpan:
// Publish the span in various locations. // Publish the span in various locations.
// This is safe to call without the lock held because the slots // This is safe to call without the lock held because the slots
// related to this span will only every be read or modified by // related to this span will only ever be read or modified by
// this thread until pointers into the span are published or // this thread until pointers into the span are published (and
// pageInUse is updated. // we execute a publication barrier at the end of this function
// before that happens) or pageInUse is updated.
h.setSpans(s.base(), npages, s) h.setSpans(s.base(), npages, s)
if !manual { if !manual {
@ -1315,6 +1316,11 @@ HaveSpan:
traceHeapAlloc() traceHeapAlloc()
} }
} }
// Make sure the newly allocated span will be observed
// by the GC before pointers into the span are published.
publicationBarrier()
return s return s
} }

View file

@ -69,6 +69,18 @@ func (a addrRange) subtract(b addrRange) addrRange {
return a return a
} }
// removeGreaterEqual removes all addresses in a greater than or equal
// to addr and returns the new range.
func (a addrRange) removeGreaterEqual(addr uintptr) addrRange {
if (offAddr{addr}).lessEqual(a.base) {
return addrRange{}
}
if a.limit.lessEqual(offAddr{addr}) {
return a
}
return makeAddrRange(a.base.addr(), addr)
}
var ( var (
// minOffAddr is the minimum address in the offset space, and // minOffAddr is the minimum address in the offset space, and
// it corresponds to the virtual address arenaBaseOffset. // it corresponds to the virtual address arenaBaseOffset.
@ -281,7 +293,7 @@ func (a *addrRanges) removeGreaterEqual(addr uintptr) {
} }
if r := a.ranges[pivot-1]; r.contains(addr) { if r := a.ranges[pivot-1]; r.contains(addr) {
removed += r.size() removed += r.size()
r = r.subtract(makeAddrRange(addr, maxOffAddr.addr())) r = r.removeGreaterEqual(addr)
if r.size() == 0 { if r.size() == 0 {
pivot-- pivot--
} else { } else {

View file

@ -293,7 +293,6 @@ func osinit() {
ncpu = getproccount() ncpu = getproccount()
physPageSize = getPageSize() physPageSize = getPageSize()
getg().m.procid = getpid() getg().m.procid = getpid()
notify(unsafe.Pointer(funcPC(sigtramp)))
} }
//go:nosplit //go:nosplit
@ -311,6 +310,9 @@ func goenvs() {
} }
func initsig(preinit bool) { func initsig(preinit bool) {
if !preinit {
notify(unsafe.Pointer(funcPC(sigtramp)))
}
} }
//go:nosplit //go:nosplit

View file

@ -533,6 +533,7 @@ func cpuinit() {
// The new G calls runtime·main. // The new G calls runtime·main.
func schedinit() { func schedinit() {
lockInit(&sched.lock, lockRankSched) lockInit(&sched.lock, lockRankSched)
lockInit(&sched.sysmonlock, lockRankSysmon)
lockInit(&sched.deferlock, lockRankDefer) lockInit(&sched.deferlock, lockRankDefer)
lockInit(&sched.sudoglock, lockRankSudog) lockInit(&sched.sudoglock, lockRankSudog)
lockInit(&deadlock, lockRankDeadlock) lockInit(&deadlock, lockRankDeadlock)
@ -4613,6 +4614,18 @@ func sysmon() {
} }
unlock(&sched.lock) unlock(&sched.lock)
} }
lock(&sched.sysmonlock)
{
// If we spent a long time blocked on sysmonlock
// then we want to update now and next since it's
// likely stale.
now1 := nanotime()
if now1-now > 50*1000 /* 50µs */ {
next, _ = timeSleepUntil()
}
now = now1
}
// trigger libc interceptors if needed // trigger libc interceptors if needed
if *cgo_yield != nil { if *cgo_yield != nil {
asmcgocall(*cgo_yield, nil) asmcgocall(*cgo_yield, nil)
@ -4665,6 +4678,7 @@ func sysmon() {
lasttrace = now lasttrace = now
schedtrace(debug.scheddetail > 0) schedtrace(debug.scheddetail > 0)
} }
unlock(&sched.sysmonlock)
} }
} }

View file

@ -108,7 +108,6 @@ import "fmt"
import "runtime" import "runtime"
var gslice []string var gslice []string
func main() { func main() {
go func() { select{} }() // ensure a second goroutine is running
mapvar := make(map[string]string, 13) mapvar := make(map[string]string, 13)
mapvar["abc"] = "def" mapvar["abc"] = "def"
mapvar["ghi"] = "jkl" mapvar["ghi"] = "jkl"
@ -231,9 +230,6 @@ func testGdbPython(t *testing.T, cgo bool) {
"-ex", "echo BEGIN goroutine 1 bt\n", "-ex", "echo BEGIN goroutine 1 bt\n",
"-ex", "goroutine 1 bt", "-ex", "goroutine 1 bt",
"-ex", "echo END\n", "-ex", "echo END\n",
"-ex", "echo BEGIN goroutine 2 bt\n",
"-ex", "goroutine 2 bt",
"-ex", "echo END\n",
"-ex", "echo BEGIN goroutine all bt\n", "-ex", "echo BEGIN goroutine all bt\n",
"-ex", "goroutine all bt", "-ex", "goroutine all bt",
"-ex", "echo END\n", "-ex", "echo END\n",
@ -310,7 +306,6 @@ func testGdbPython(t *testing.T, cgo bool) {
// Check that the backtraces are well formed. // Check that the backtraces are well formed.
checkCleanBacktrace(t, blocks["goroutine 1 bt"]) checkCleanBacktrace(t, blocks["goroutine 1 bt"])
checkCleanBacktrace(t, blocks["goroutine 2 bt"])
checkCleanBacktrace(t, blocks["goroutine 1 bt at the end"]) checkCleanBacktrace(t, blocks["goroutine 1 bt at the end"])
btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`) btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
@ -318,12 +313,7 @@ func testGdbPython(t *testing.T, cgo bool) {
t.Fatalf("goroutine 1 bt failed: %s", bl) t.Fatalf("goroutine 1 bt failed: %s", bl)
} }
btGoroutine2Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?runtime.+at`) if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) {
if bl := blocks["goroutine 2 bt"]; !btGoroutine2Re.MatchString(bl) {
t.Fatalf("goroutine 2 bt failed: %s", bl)
}
if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) || !btGoroutine2Re.MatchString(bl) {
t.Fatalf("goroutine all bt failed: %s", bl) t.Fatalf("goroutine all bt failed: %s", bl)
} }

View file

@ -349,9 +349,9 @@ type sudog struct {
g *g g *g
next *sudog next *sudog
prev *sudog prev *sudog
elem unsafe.Pointer // data element (may point to stack) elem unsafe.Pointer // data element (may point to stack)
// The following fields are never accessed concurrently. // The following fields are never accessed concurrently.
// For channels, waitlink is only accessed by g. // For channels, waitlink is only accessed by g.
@ -366,10 +366,10 @@ type sudog struct {
// g.selectDone must be CAS'd to win the wake-up race. // g.selectDone must be CAS'd to win the wake-up race.
isSelect bool isSelect bool
parent *sudog // semaRoot binary tree parent *sudog // semaRoot binary tree
waitlink *sudog // g.waiting list or semaRoot waitlink *sudog // g.waiting list or semaRoot
waittail *sudog // semaRoot waittail *sudog // semaRoot
c *hchan // channel c *hchan // channel
} }
type libcall struct { type libcall struct {
@ -768,6 +768,12 @@ type schedt struct {
procresizetime int64 // nanotime() of last change to gomaxprocs procresizetime int64 // nanotime() of last change to gomaxprocs
totaltime int64 // ∫gomaxprocs dt up to procresizetime totaltime int64 // ∫gomaxprocs dt up to procresizetime
// sysmonlock protects sysmon's actions on the runtime.
//
// Acquire and hold this mutex to block sysmon from interacting
// with the rest of the runtime.
sysmonlock mutex
} }
// Values for the flags field of a sigTabT. // Values for the flags field of a sigTabT.

View file

@ -32,11 +32,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@ -71,14 +71,15 @@ func EINTR() {
// spin does CPU bound spinning and allocating for a millisecond, // spin does CPU bound spinning and allocating for a millisecond,
// to get a SIGURG. // to get a SIGURG.
//go:noinline //go:noinline
func spin() (float64, [][]byte) { func spin() (float64, []byte) {
stop := time.Now().Add(time.Millisecond) stop := time.Now().Add(time.Millisecond)
r1 := 0.0 r1 := 0.0
var r2 [][]byte r2 := make([]byte, 200)
for time.Now().Before(stop) { for time.Now().Before(stop) {
for i := 1; i < 1e6; i++ { for i := 1; i < 1e6; i++ {
r1 += r1 / float64(i) r1 += r1 / float64(i)
r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)) r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)...)
r2 = r2[100:]
} }
} }
return r1, r2 return r1, r2
@ -96,8 +97,13 @@ func winch() {
// sendSomeSignals triggers a few SIGURG and SIGWINCH signals. // sendSomeSignals triggers a few SIGURG and SIGWINCH signals.
func sendSomeSignals() { func sendSomeSignals() {
spin() done := make(chan struct{})
go func() {
spin()
close(done)
}()
winch() winch()
<-done
} }
// testPipe tests pipe operations. // testPipe tests pipe operations.
@ -212,6 +218,10 @@ func testExec(wg *sync.WaitGroup) {
go func() { go func() {
defer wg.Done() defer wg.Done()
cmd := exec.Command(os.Args[0], "Block") cmd := exec.Command(os.Args[0], "Block")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stderr = new(bytes.Buffer) cmd.Stderr = new(bytes.Buffer)
cmd.Stdout = cmd.Stderr cmd.Stdout = cmd.Stderr
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
@ -220,9 +230,7 @@ func testExec(wg *sync.WaitGroup) {
go func() { go func() {
sendSomeSignals() sendSomeSignals()
if err := cmd.Process.Signal(os.Interrupt); err != nil { stdin.Close()
panic(err)
}
}() }()
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
@ -231,10 +239,7 @@ func testExec(wg *sync.WaitGroup) {
}() }()
} }
// Block blocks until the process receives os.Interrupt. // Block blocks until stdin is closed.
func Block() { func Block() {
c := make(chan os.Signal, 1) io.Copy(ioutil.Discard, os.Stdin)
signal.Notify(c, os.Interrupt)
defer signal.Stop(c)
<-c
} }

View file

@ -187,6 +187,9 @@ func StartTrace() error {
// paired with an end). // paired with an end).
stopTheWorldGC("start tracing") stopTheWorldGC("start tracing")
// Prevent sysmon from running any code that could generate events.
lock(&sched.sysmonlock)
// We are in stop-the-world, but syscalls can finish and write to trace concurrently. // We are in stop-the-world, but syscalls can finish and write to trace concurrently.
// Exitsyscall could check trace.enabled long before and then suddenly wake up // Exitsyscall could check trace.enabled long before and then suddenly wake up
// and decide to write to trace at a random point in time. // and decide to write to trace at a random point in time.
@ -196,6 +199,7 @@ func StartTrace() error {
if trace.enabled || trace.shutdown { if trace.enabled || trace.shutdown {
unlock(&trace.bufLock) unlock(&trace.bufLock)
unlock(&sched.sysmonlock)
startTheWorldGC() startTheWorldGC()
return errorString("tracing is already enabled") return errorString("tracing is already enabled")
} }
@ -267,6 +271,8 @@ func StartTrace() error {
unlock(&trace.bufLock) unlock(&trace.bufLock)
unlock(&sched.sysmonlock)
startTheWorldGC() startTheWorldGC()
return nil return nil
} }
@ -278,11 +284,15 @@ func StopTrace() {
// and also to avoid races with traceEvent. // and also to avoid races with traceEvent.
stopTheWorldGC("stop tracing") stopTheWorldGC("stop tracing")
// See the comment in StartTrace.
lock(&sched.sysmonlock)
// See the comment in StartTrace. // See the comment in StartTrace.
lock(&trace.bufLock) lock(&trace.bufLock)
if !trace.enabled { if !trace.enabled {
unlock(&trace.bufLock) unlock(&trace.bufLock)
unlock(&sched.sysmonlock)
startTheWorldGC() startTheWorldGC()
return return
} }
@ -320,6 +330,8 @@ func StopTrace() {
trace.shutdown = true trace.shutdown = true
unlock(&trace.bufLock) unlock(&trace.bufLock)
unlock(&sched.sysmonlock)
startTheWorldGC() startTheWorldGC()
// The world is started but we've set trace.shutdown, so new tracing can't start. // The world is started but we've set trace.shutdown, so new tracing can't start.

View file

@ -339,6 +339,26 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
var attrs uint32 = FILE_ATTRIBUTE_NORMAL var attrs uint32 = FILE_ATTRIBUTE_NORMAL
if perm&S_IWRITE == 0 { if perm&S_IWRITE == 0 {
attrs = FILE_ATTRIBUTE_READONLY attrs = FILE_ATTRIBUTE_READONLY
if createmode == CREATE_ALWAYS {
// We have been asked to create a read-only file.
// If the file already exists, the semantics of
// the Unix open system call is to preserve the
// existing permissions. If we pass CREATE_ALWAYS
// and FILE_ATTRIBUTE_READONLY to CreateFile,
// and the file already exists, CreateFile will
// change the file permissions.
// Avoid that to preserve the Unix semantics.
h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
switch e {
case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND:
// File does not exist. These are the same
// errors as Errno.Is checks for ErrNotExist.
// Carry on to create the file.
default:
// Success or some different error.
return h, e
}
}
} }
h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0) h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
return h, e return h, e

View file

@ -372,6 +372,7 @@ type common struct {
tempDirOnce sync.Once tempDirOnce sync.Once
tempDir string tempDir string
tempDirErr error tempDirErr error
tempDirSeq int32
} }
// Short reports whether the -test.short flag is set. // Short reports whether the -test.short flag is set.
@ -821,12 +822,13 @@ var tempDirReplacer struct {
} }
// TempDir returns a temporary directory for the test to use. // TempDir returns a temporary directory for the test to use.
// It is lazily created on first access, and calls t.Fatal if the directory
// creation fails.
// Subsequent calls to t.TempDir return the same directory.
// The directory is automatically removed by Cleanup when the test and // The directory is automatically removed by Cleanup when the test and
// all its subtests complete. // all its subtests complete.
// Each subsequent call to t.TempDir returns a unique directory;
// if the directory creation fails, TempDir terminates the test by calling Fatal.
func (c *common) TempDir() string { func (c *common) TempDir() string {
// Use a single parent directory for all the temporary directories
// created by a test, each numbered sequentially.
c.tempDirOnce.Do(func() { c.tempDirOnce.Do(func() {
c.Helper() c.Helper()
@ -849,7 +851,12 @@ func (c *common) TempDir() string {
if c.tempDirErr != nil { if c.tempDirErr != nil {
c.Fatalf("TempDir: %v", c.tempDirErr) c.Fatalf("TempDir: %v", c.tempDirErr)
} }
return c.tempDir seq := atomic.AddInt32(&c.tempDirSeq, 1)
dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq)
if err := os.Mkdir(dir, 0777); err != nil {
c.Fatalf("TempDir: %v", err)
}
return dir
} }
// panicHanding is an argument to runCleanup. // panicHanding is an argument to runCleanup.

View file

@ -7,6 +7,7 @@ package testing_test
import ( import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"testing" "testing"
) )
@ -55,8 +56,11 @@ func testTempDir(t *testing.T) {
t.Fatal("expected dir") t.Fatal("expected dir")
} }
dir2 := t.TempDir() dir2 := t.TempDir()
if dir != dir2 { if dir == dir2 {
t.Fatal("directory changed between calls") t.Fatal("subsequent calls to TempDir returned the same directory")
}
if filepath.Dir(dir) != filepath.Dir(dir2) {
t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2)
} }
dirCh <- dir dirCh <- dir
fi, err := os.Stat(dir) fi, err := os.Stat(dir)