mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.link] all: merge branch 'master' into dev.link
Change-Id: I85b653b621ad8cb2ef27886210ea2c4b7409b60d
This commit is contained in:
commit
6097f7cf7a
45 changed files with 485 additions and 151 deletions
103
doc/go1.15.html
103
doc/go1.15.html
|
|
@ -33,6 +33,12 @@ TODO
|
|||
|
||||
<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 -->
|
||||
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,
|
||||
|
|
@ -116,6 +122,73 @@ TODO
|
|||
<code>GODEBUG=modcacheunzipinplace=1</code>.
|
||||
</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>
|
||||
|
||||
<p>
|
||||
|
|
@ -155,22 +228,6 @@ TODO
|
|||
TODO
|
||||
</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>
|
||||
|
||||
<p>
|
||||
|
|
@ -375,6 +432,20 @@ TODO
|
|||
|
||||
<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
|
||||
<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 -->
|
||||
The new methods
|
||||
<a href="/pkg/testing/#T.TempDir"><code>T.TempDir</code></a> and
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ These default to the values of <code>$GOHOSTOS</code> and
|
|||
|
||||
<p>
|
||||
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>linux</code>, <code>netbsd</code>, <code>openbsd</code>,
|
||||
<code>plan9</code>, <code>solaris</code> and <code>windows</code>.
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ func compile(fn *Node) {
|
|||
}
|
||||
}
|
||||
|
||||
if compilenow() {
|
||||
if compilenow(fn) {
|
||||
compileSSA(fn, 0)
|
||||
} else {
|
||||
compilequeue = append(compilequeue, fn)
|
||||
|
|
@ -282,10 +282,31 @@ func compile(fn *Node) {
|
|||
// If functions are not compiled immediately,
|
||||
// they are enqueued in compilequeue,
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// compileSSA builds an SSA backend function,
|
||||
|
|
|
|||
11
src/cmd/dist/build.go
vendored
11
src/cmd/dist/build.go
vendored
|
|
@ -31,7 +31,6 @@ var (
|
|||
goos string
|
||||
goarm string
|
||||
go386 string
|
||||
goamd64 string
|
||||
gomips string
|
||||
gomips64 string
|
||||
goppc64 string
|
||||
|
|
@ -152,12 +151,6 @@ func xinit() {
|
|||
}
|
||||
go386 = b
|
||||
|
||||
b = os.Getenv("GOAMD64")
|
||||
if b == "" {
|
||||
b = "alignedjumps"
|
||||
}
|
||||
goamd64 = b
|
||||
|
||||
b = os.Getenv("GOMIPS")
|
||||
if b == "" {
|
||||
b = "hardfloat"
|
||||
|
|
@ -230,7 +223,6 @@ func xinit() {
|
|||
|
||||
// For tools being invoked but also for os.ExpandEnv.
|
||||
os.Setenv("GO386", go386)
|
||||
os.Setenv("GOAMD64", goamd64)
|
||||
os.Setenv("GOARCH", goarch)
|
||||
os.Setenv("GOARM", goarm)
|
||||
os.Setenv("GOHOSTARCH", gohostarch)
|
||||
|
|
@ -1171,9 +1163,6 @@ func cmdenv() {
|
|||
if goarch == "386" {
|
||||
xprintf(format, "GO386", go386)
|
||||
}
|
||||
if goarch == "amd64" {
|
||||
xprintf(format, "GOAMD64", goamd64)
|
||||
}
|
||||
if goarch == "mips" || goarch == "mipsle" {
|
||||
xprintf(format, "GOMIPS", gomips)
|
||||
}
|
||||
|
|
|
|||
2
src/cmd/dist/buildruntime.go
vendored
2
src/cmd/dist/buildruntime.go
vendored
|
|
@ -42,7 +42,6 @@ func mkzversion(dir, file string) {
|
|||
//
|
||||
// const defaultGOROOT = <goroot>
|
||||
// const defaultGO386 = <go386>
|
||||
// const defaultGOAMD64 = <goamd64>
|
||||
// const defaultGOARM = <goarm>
|
||||
// const defaultGOMIPS = <gomips>
|
||||
// const defaultGOMIPS64 = <gomips64>
|
||||
|
|
@ -72,7 +71,6 @@ func mkzbootstrap(file string) {
|
|||
fmt.Fprintf(&buf, "import \"runtime\"\n")
|
||||
fmt.Fprintln(&buf)
|
||||
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 defaultGOMIPS = `%s`\n", gomips)
|
||||
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
|
||||
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d
|
||||
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/tools v0.0.0-20200504152539-33427f1b0364
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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.2.1-0.20200429172858-859b3ef565e2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
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-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=
|
||||
|
|
|
|||
|
|
@ -1754,9 +1754,6 @@
|
|||
// GO386
|
||||
// For GOARCH=386, the floating point instruction set.
|
||||
// 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
|
||||
// For GOARCH=mips{,le}, whether to use floating point instructions.
|
||||
// Valid values are hardfloat (default), softfloat.
|
||||
|
|
|
|||
|
|
@ -241,7 +241,6 @@ var (
|
|||
// Used in envcmd.MkEnv and build ID computations.
|
||||
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
|
||||
GO386 = envOr("GO386", objabi.GO386)
|
||||
GOAMD64 = envOr("GOAMD64", objabi.GOAMD64)
|
||||
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
|
||||
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
|
||||
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
|
||||
|
|
@ -267,8 +266,6 @@ func GetArchEnv() (key, val string) {
|
|||
return "GOARM", GOARM
|
||||
case "386":
|
||||
return "GO386", GO386
|
||||
case "amd64":
|
||||
return "GOAMD64", GOAMD64
|
||||
case "mips", "mipsle":
|
||||
return "GOMIPS", GOMIPS
|
||||
case "mips64", "mips64le":
|
||||
|
|
|
|||
|
|
@ -582,9 +582,6 @@ Architecture-specific environment variables:
|
|||
GO386
|
||||
For GOARCH=386, the floating point instruction set.
|
||||
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
|
||||
For GOARCH=mips{,le}, whether to use floating point instructions.
|
||||
Valid values are hardfloat (default), softfloat.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
// errors are best, followed by proxy errors other than ErrNotExist, followed
|
||||
// by ErrNotExist. Note that errProxyOff, errNoproxy, and errUseProxy are
|
||||
// equivalent to ErrNotExist.
|
||||
// by 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 (
|
||||
notExistRank = iota
|
||||
proxyRank
|
||||
|
|
@ -212,7 +216,7 @@ func TryProxies(f func(proxy string) error) error {
|
|||
}
|
||||
isNotExistErr := errors.Is(err, os.ErrNotExist)
|
||||
|
||||
if proxy.url == "direct" || proxy.url == "noproxy" {
|
||||
if proxy.url == "direct" || (proxy.url == "noproxy" && err != errUseProxy) {
|
||||
bestErr = err
|
||||
bestErrRank = directRank
|
||||
} else if bestErrRank <= proxyRank && !isNotExistErr {
|
||||
|
|
|
|||
|
|
@ -2434,13 +2434,25 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
|
|||
if b.flagCache == nil {
|
||||
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
|
||||
// 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
|
||||
// version of GCC, so some systems have frozen on it.
|
||||
// Now we pass an empty file on stdin, which should work at least for
|
||||
// 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 {
|
||||
b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
|
||||
if cfg.BuildN {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func TestNoteReading(t *testing.T) {
|
|||
// 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")})
|
||||
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
|
||||
// ld.gold is missing, see issue #22340.
|
||||
t.Log("skipping gold test")
|
||||
|
|
|
|||
6
src/cmd/go/testdata/script/issue36000.txt
vendored
Normal file
6
src/cmd/go/testdata/script/issue36000.txt
vendored
Normal 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
|
||||
10
src/cmd/go/testdata/script/mod_gonoproxy.txt
vendored
10
src/cmd/go/testdata/script/mod_gonoproxy.txt
vendored
|
|
@ -10,7 +10,7 @@ env GOSUMDB=$sumdb' '$proxy/sumdb-wrong
|
|||
! go get rsc.io/quote
|
||||
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'
|
||||
go get rsc.io/quote
|
||||
rm go.sum
|
||||
|
|
@ -18,7 +18,13 @@ env GOPRIVATE='*/quote,*/*mple*,golang.org/x'
|
|||
env GONOPROXY=none # that is, proxy all despite GOPRIVATE
|
||||
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
|
||||
[!exec:git] skip
|
||||
env GOPRIVATE=none
|
||||
|
|
|
|||
|
|
@ -425,6 +425,14 @@ func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) {
|
|||
absfn.Type = objabi.SDWARFINFO
|
||||
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}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,16 +37,13 @@ var (
|
|||
|
||||
const (
|
||||
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 {
|
||||
switch v := envOr("GOAMD64", defaultGOAMD64); v {
|
||||
case "normaljumps", "alignedjumps":
|
||||
return v
|
||||
}
|
||||
log.Fatalf("Invalid GOAMD64 value. Must be normaljumps or alignedjumps.")
|
||||
panic("unreachable")
|
||||
return Go115AMD64
|
||||
}
|
||||
|
||||
func goarm() int {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
var tramp loader.Sym
|
||||
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)))
|
||||
if ldr.SymType(tramp) == sym.SDYNIMPORT {
|
||||
// don't reuse trampoline defined in other module
|
||||
continue
|
||||
}
|
||||
if oName == "runtime.deferreturn" {
|
||||
ldr.SetIsDeferReturnTramp(tramp, true)
|
||||
}
|
||||
if ldr.SymValue(tramp) == 0 {
|
||||
// either the trampoline does not exist -- we need to create one,
|
||||
// or found one the address which is not assigned -- this will be
|
||||
|
|
|
|||
50
src/cmd/link/internal/ld/fallocate_test.go
Normal file
50
src/cmd/link/internal/ld/fallocate_test.go
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -10,16 +10,25 @@ import (
|
|||
)
|
||||
|
||||
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{
|
||||
Flags: syscall.F_ALLOCATEALL,
|
||||
Posmode: syscall.F_PEOFPOSMODE,
|
||||
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)))
|
||||
if err != 0 {
|
||||
return err
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store)))
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32
|
|||
// set the resumption point to PC_B.
|
||||
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() {
|
||||
deferreturn = lastWasmAddr - 1
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -229,7 +229,8 @@ type Loader struct {
|
|||
outdata [][]byte // symbol's data in the output buffer
|
||||
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
|
||||
|
||||
|
|
@ -354,6 +355,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
|
|||
attrCgoExportDynamic: make(map[Sym]struct{}),
|
||||
attrCgoExportStatic: make(map[Sym]struct{}),
|
||||
itablink: make(map[Sym]struct{}),
|
||||
deferReturnTramp: make(map[Sym]bool),
|
||||
extStaticSyms: make(map[nameVer]Sym),
|
||||
builtinSyms: make([]Sym, nbuiltin),
|
||||
flags: flags,
|
||||
|
|
@ -1050,6 +1052,16 @@ func (l *Loader) IsItabLink(i Sym) bool {
|
|||
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.
|
||||
func (l *Loader) growValues(reqLen int) {
|
||||
curLen := len(l.values)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// distinct trampolines.
|
||||
|
||||
name := ldr.SymName(rs)
|
||||
oName := ldr.SymName(rs)
|
||||
name := oName
|
||||
if r.Add() == 0 {
|
||||
name = name + fmt.Sprintf("-tramp%d", i)
|
||||
name += fmt.Sprintf("-tramp%d", i)
|
||||
} 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
|
||||
|
||||
tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
|
||||
if oName == "runtime.deferreturn" {
|
||||
ldr.SetIsDeferReturnTramp(tramp, true)
|
||||
}
|
||||
if ldr.SymValue(tramp) == 0 {
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -605,10 +605,23 @@ func TestFuncAlign(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
const helloSrc = `
|
||||
const testTrampSrc = `
|
||||
package main
|
||||
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) {
|
||||
|
|
@ -631,7 +644,7 @@ func TestTrampoline(t *testing.T) {
|
|||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
src := filepath.Join(tmpdir, "hello.go")
|
||||
err = ioutil.WriteFile(src, []byte(helloSrc), 0666)
|
||||
err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
|
|
@ -29,7 +29,7 @@ golang.org/x/arch/x86/x86asm
|
|||
golang.org/x/crypto/ed25519
|
||||
golang.org/x/crypto/ed25519/internal/edwards25519
|
||||
golang.org/x/crypto/ssh/terminal
|
||||
# golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2
|
||||
# golang.org/x/mod v0.3.0
|
||||
## explicit
|
||||
golang.org/x/mod/internal/lazyregexp
|
||||
golang.org/x/mod/modfile
|
||||
|
|
|
|||
|
|
@ -980,7 +980,28 @@ func testResumption(t *testing.T, version uint16) {
|
|||
if bytes.Equal(ticket, getTicket()) {
|
||||
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
|
||||
// before the serverConfig is used works.
|
||||
|
|
|
|||
|
|
@ -75,13 +75,8 @@ func (hs *serverHandshakeState) handshake() error {
|
|||
if err := hs.establishKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
// 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 err := hs.sendSessionTicket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendSessionTicket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendFinished(c.serverFinished[:]); err != nil {
|
||||
return err
|
||||
|
|
@ -688,6 +683,9 @@ func (hs *serverHandshakeState) readFinished(out []byte) 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 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -695,6 +693,13 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
|
|||
c := hs.c
|
||||
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
|
||||
for _, cert := range c.peerCertificates {
|
||||
certsFromClient = append(certsFromClient, cert.Raw)
|
||||
|
|
@ -702,7 +707,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
|
|||
state := sessionState{
|
||||
vers: c.vers,
|
||||
cipherSuite: hs.suite.id,
|
||||
createdAt: uint64(c.config.time().Unix()),
|
||||
createdAt: createdAt,
|
||||
masterSecret: hs.masterSecret,
|
||||
certificates: certsFromClient,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ func (m *sessionState) unmarshal(data []byte) bool {
|
|||
*m = sessionState{usedOldKey: m.usedOldKey}
|
||||
s := cryptobyte.String(data)
|
||||
if ok := s.ReadUint16(&m.vers) &&
|
||||
m.vers != VersionTLS13 &&
|
||||
s.ReadUint16(&m.cipherSuite) &&
|
||||
readUint64(&s, &m.createdAt) &&
|
||||
readUint16LengthPrefixed(&s, &m.masterSecret) &&
|
||||
|
|
|
|||
|
|
@ -204,7 +204,8 @@ func TestLoadSystemCertsLoadColonSeparatedDirs(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 {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
|
|
@ -216,7 +217,7 @@ func TestReadUniqueDirectoryEntries(t *testing.T) {
|
|||
if err := os.Symlink("../target-out", temp("link-out")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := readUniqueDirectoryEntries(t.TempDir())
|
||||
got, err := readUniqueDirectoryEntries(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ const KnownEnv = `
|
|||
GCCGO
|
||||
GO111MODULE
|
||||
GO386
|
||||
GOAMD64
|
||||
GOARCH
|
||||
GOARM
|
||||
GOBIN
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ func TestLinkerGC(t *testing.T) {
|
|||
t.Skip("skipping in short mode")
|
||||
}
|
||||
t.Parallel()
|
||||
tmp := t.TempDir()
|
||||
goBin := testenv.GoToolPath(t)
|
||||
goFile := filepath.Join(t.TempDir(), "x.go")
|
||||
goFile := filepath.Join(tmp, "x.go")
|
||||
file := []byte(`package main
|
||||
import _ "math/big"
|
||||
func main() {}
|
||||
|
|
@ -30,13 +31,13 @@ func main() {}
|
|||
t.Fatal(err)
|
||||
}
|
||||
cmd := exec.Command(goBin, "build", "-o", "x.exe", "x.go")
|
||||
cmd.Dir = t.TempDir()
|
||||
cmd.Dir = tmp
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("compile: %v, %s", err, out)
|
||||
}
|
||||
|
||||
cmd = exec.Command(goBin, "tool", "nm", "x.exe")
|
||||
cmd.Dir = t.TempDir()
|
||||
cmd.Dir = tmp
|
||||
nm, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("nm: %v, %s", err, nm)
|
||||
|
|
|
|||
|
|
@ -2539,3 +2539,34 @@ func isDeadlineExceeded(err error) bool {
|
|||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,14 +249,15 @@ func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte
|
|||
t.Helper()
|
||||
|
||||
hook = hookCopyFileRange(t)
|
||||
tmp := t.TempDir()
|
||||
|
||||
src, err := Create(filepath.Join(t.TempDir(), "src"))
|
||||
src, err := Create(filepath.Join(tmp, "src"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { src.Close() })
|
||||
|
||||
dst, err = Create(filepath.Join(t.TempDir(), "dst"))
|
||||
dst, err = Create(filepath.Join(tmp, "dst"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ const (
|
|||
lockRankDummy lockRank = iota
|
||||
|
||||
// Locks held above sched
|
||||
lockRankSysmon
|
||||
lockRankScavenge
|
||||
lockRankForcegc
|
||||
lockRankSweepWaiters
|
||||
|
|
@ -113,6 +114,7 @@ const lockRankLeafRank lockRank = 1000
|
|||
var lockNames = []string{
|
||||
lockRankDummy: "",
|
||||
|
||||
lockRankSysmon: "sysmon",
|
||||
lockRankScavenge: "scavenge",
|
||||
lockRankForcegc: "forcegc",
|
||||
lockRankSweepWaiters: "sweepWaiters",
|
||||
|
|
@ -194,48 +196,49 @@ func (rank lockRank) String() string {
|
|||
// hchan lock can be held immediately above it when it is acquired.
|
||||
var lockPartialOrder [][]lockRank = [][]lockRank{
|
||||
lockRankDummy: {},
|
||||
lockRankScavenge: {},
|
||||
lockRankForcegc: {},
|
||||
lockRankSysmon: {},
|
||||
lockRankScavenge: {lockRankSysmon},
|
||||
lockRankForcegc: {lockRankSysmon},
|
||||
lockRankSweepWaiters: {},
|
||||
lockRankAssistQueue: {},
|
||||
lockRankCpuprof: {},
|
||||
lockRankSweep: {},
|
||||
lockRankSched: {lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep},
|
||||
lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep},
|
||||
lockRankDeadlock: {lockRankDeadlock},
|
||||
lockRankPanic: {lockRankDeadlock},
|
||||
lockRankAllg: {lockRankSched, lockRankPanic},
|
||||
lockRankAllp: {lockRankSched},
|
||||
lockRankAllg: {lockRankSysmon, lockRankSched, lockRankPanic},
|
||||
lockRankAllp: {lockRankSysmon, lockRankSched},
|
||||
lockRankPollDesc: {},
|
||||
lockRankTimers: {lockRankScavenge, lockRankSched, lockRankAllp, lockRankPollDesc, lockRankTimers},
|
||||
lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankSched, lockRankAllp, lockRankPollDesc, lockRankTimers},
|
||||
lockRankItab: {},
|
||||
lockRankReflectOffs: {lockRankItab},
|
||||
lockRankHchan: {lockRankScavenge, lockRankSweep, lockRankHchan},
|
||||
lockRankFin: {lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan},
|
||||
lockRankNotifyList: {},
|
||||
lockRankTraceBuf: {lockRankScavenge},
|
||||
lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
|
||||
lockRankTraceStrings: {lockRankTraceBuf},
|
||||
lockRankMspanSpecial: {lockRankScavenge, 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},
|
||||
lockRankGcBitsArenas: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
|
||||
lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings},
|
||||
lockRankProf: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
|
||||
lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
|
||||
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},
|
||||
lockRankNetpollInit: {lockRankTimers},
|
||||
|
||||
lockRankRwmutexW: {},
|
||||
lockRankRwmutexR: {lockRankRwmutexW},
|
||||
|
||||
lockRankMcentral: {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},
|
||||
lockRankSpanSetSpine: {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},
|
||||
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},
|
||||
lockRankStackLarge: {lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankSpanSetSpine, lockRankGscan},
|
||||
lockRankMcentral: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
|
||||
lockRankSpine: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, 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: {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: {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: {lockRankSysmon, lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankSpanSetSpine, lockRankGscan},
|
||||
lockRankDefer: {},
|
||||
lockRankSudog: {lockRankNotifyList, lockRankHchan},
|
||||
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},
|
||||
lockRankMheapSpecial: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
|
||||
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: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan},
|
||||
lockRankGlobalAlloc: {lockRankProf, lockRankSpine, lockRankSpanSetSpine, lockRankMheap, lockRankMheapSpecial},
|
||||
|
||||
lockRankGFree: {lockRankSched},
|
||||
|
|
|
|||
|
|
@ -1283,9 +1283,10 @@ HaveSpan:
|
|||
// Publish the span in various locations.
|
||||
|
||||
// This is safe to call without the lock held because the slots
|
||||
// related to this span will only every be read or modified by
|
||||
// this thread until pointers into the span are published or
|
||||
// pageInUse is updated.
|
||||
// related to this span will only ever be read or modified by
|
||||
// this thread until pointers into the span are published (and
|
||||
// we execute a publication barrier at the end of this function
|
||||
// before that happens) or pageInUse is updated.
|
||||
h.setSpans(s.base(), npages, s)
|
||||
|
||||
if !manual {
|
||||
|
|
@ -1315,6 +1316,11 @@ HaveSpan:
|
|||
traceHeapAlloc()
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the newly allocated span will be observed
|
||||
// by the GC before pointers into the span are published.
|
||||
publicationBarrier()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,18 @@ func (a addrRange) subtract(b addrRange) addrRange {
|
|||
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 (
|
||||
// minOffAddr is the minimum address in the offset space, and
|
||||
// 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) {
|
||||
removed += r.size()
|
||||
r = r.subtract(makeAddrRange(addr, maxOffAddr.addr()))
|
||||
r = r.removeGreaterEqual(addr)
|
||||
if r.size() == 0 {
|
||||
pivot--
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -293,7 +293,6 @@ func osinit() {
|
|||
ncpu = getproccount()
|
||||
physPageSize = getPageSize()
|
||||
getg().m.procid = getpid()
|
||||
notify(unsafe.Pointer(funcPC(sigtramp)))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
|
|
@ -311,6 +310,9 @@ func goenvs() {
|
|||
}
|
||||
|
||||
func initsig(preinit bool) {
|
||||
if !preinit {
|
||||
notify(unsafe.Pointer(funcPC(sigtramp)))
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
|
|
|
|||
|
|
@ -533,6 +533,7 @@ func cpuinit() {
|
|||
// The new G calls runtime·main.
|
||||
func schedinit() {
|
||||
lockInit(&sched.lock, lockRankSched)
|
||||
lockInit(&sched.sysmonlock, lockRankSysmon)
|
||||
lockInit(&sched.deferlock, lockRankDefer)
|
||||
lockInit(&sched.sudoglock, lockRankSudog)
|
||||
lockInit(&deadlock, lockRankDeadlock)
|
||||
|
|
@ -4613,6 +4614,18 @@ func sysmon() {
|
|||
}
|
||||
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
|
||||
if *cgo_yield != nil {
|
||||
asmcgocall(*cgo_yield, nil)
|
||||
|
|
@ -4665,6 +4678,7 @@ func sysmon() {
|
|||
lasttrace = now
|
||||
schedtrace(debug.scheddetail > 0)
|
||||
}
|
||||
unlock(&sched.sysmonlock)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ import "fmt"
|
|||
import "runtime"
|
||||
var gslice []string
|
||||
func main() {
|
||||
go func() { select{} }() // ensure a second goroutine is running
|
||||
mapvar := make(map[string]string, 13)
|
||||
mapvar["abc"] = "def"
|
||||
mapvar["ghi"] = "jkl"
|
||||
|
|
@ -231,9 +230,6 @@ func testGdbPython(t *testing.T, cgo bool) {
|
|||
"-ex", "echo BEGIN goroutine 1 bt\n",
|
||||
"-ex", "goroutine 1 bt",
|
||||
"-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", "goroutine all bt",
|
||||
"-ex", "echo END\n",
|
||||
|
|
@ -310,7 +306,6 @@ func testGdbPython(t *testing.T, cgo bool) {
|
|||
|
||||
// Check that the backtraces are well formed.
|
||||
checkCleanBacktrace(t, blocks["goroutine 1 bt"])
|
||||
checkCleanBacktrace(t, blocks["goroutine 2 bt"])
|
||||
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`)
|
||||
|
|
@ -318,12 +313,7 @@ func testGdbPython(t *testing.T, cgo bool) {
|
|||
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 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) {
|
||||
if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) {
|
||||
t.Fatalf("goroutine all bt failed: %s", bl)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -349,9 +349,9 @@ type sudog struct {
|
|||
|
||||
g *g
|
||||
|
||||
next *sudog
|
||||
prev *sudog
|
||||
elem unsafe.Pointer // data element (may point to stack)
|
||||
next *sudog
|
||||
prev *sudog
|
||||
elem unsafe.Pointer // data element (may point to stack)
|
||||
|
||||
// The following fields are never accessed concurrently.
|
||||
// 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.
|
||||
isSelect bool
|
||||
|
||||
parent *sudog // semaRoot binary tree
|
||||
waitlink *sudog // g.waiting list or semaRoot
|
||||
waittail *sudog // semaRoot
|
||||
c *hchan // channel
|
||||
parent *sudog // semaRoot binary tree
|
||||
waitlink *sudog // g.waiting list or semaRoot
|
||||
waittail *sudog // semaRoot
|
||||
c *hchan // channel
|
||||
}
|
||||
|
||||
type libcall struct {
|
||||
|
|
@ -768,6 +768,12 @@ type schedt struct {
|
|||
|
||||
procresizetime int64 // nanotime() of last change to gomaxprocs
|
||||
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.
|
||||
|
|
|
|||
31
src/runtime/testdata/testprogcgo/eintr.go
vendored
31
src/runtime/testdata/testprogcgo/eintr.go
vendored
|
|
@ -32,11 +32,11 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
|
@ -71,14 +71,15 @@ func EINTR() {
|
|||
// spin does CPU bound spinning and allocating for a millisecond,
|
||||
// to get a SIGURG.
|
||||
//go:noinline
|
||||
func spin() (float64, [][]byte) {
|
||||
func spin() (float64, []byte) {
|
||||
stop := time.Now().Add(time.Millisecond)
|
||||
r1 := 0.0
|
||||
var r2 [][]byte
|
||||
r2 := make([]byte, 200)
|
||||
for time.Now().Before(stop) {
|
||||
for i := 1; i < 1e6; 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
|
||||
|
|
@ -96,8 +97,13 @@ func winch() {
|
|||
|
||||
// sendSomeSignals triggers a few SIGURG and SIGWINCH signals.
|
||||
func sendSomeSignals() {
|
||||
spin()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
spin()
|
||||
close(done)
|
||||
}()
|
||||
winch()
|
||||
<-done
|
||||
}
|
||||
|
||||
// testPipe tests pipe operations.
|
||||
|
|
@ -212,6 +218,10 @@ func testExec(wg *sync.WaitGroup) {
|
|||
go func() {
|
||||
defer wg.Done()
|
||||
cmd := exec.Command(os.Args[0], "Block")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cmd.Stderr = new(bytes.Buffer)
|
||||
cmd.Stdout = cmd.Stderr
|
||||
if err := cmd.Start(); err != nil {
|
||||
|
|
@ -220,9 +230,7 @@ func testExec(wg *sync.WaitGroup) {
|
|||
|
||||
go func() {
|
||||
sendSomeSignals()
|
||||
if err := cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stdin.Close()
|
||||
}()
|
||||
|
||||
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() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
defer signal.Stop(c)
|
||||
<-c
|
||||
io.Copy(ioutil.Discard, os.Stdin)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ func StartTrace() error {
|
|||
// paired with an end).
|
||||
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.
|
||||
// Exitsyscall could check trace.enabled long before and then suddenly wake up
|
||||
// and decide to write to trace at a random point in time.
|
||||
|
|
@ -196,6 +199,7 @@ func StartTrace() error {
|
|||
|
||||
if trace.enabled || trace.shutdown {
|
||||
unlock(&trace.bufLock)
|
||||
unlock(&sched.sysmonlock)
|
||||
startTheWorldGC()
|
||||
return errorString("tracing is already enabled")
|
||||
}
|
||||
|
|
@ -267,6 +271,8 @@ func StartTrace() error {
|
|||
|
||||
unlock(&trace.bufLock)
|
||||
|
||||
unlock(&sched.sysmonlock)
|
||||
|
||||
startTheWorldGC()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -278,11 +284,15 @@ func StopTrace() {
|
|||
// and also to avoid races with traceEvent.
|
||||
stopTheWorldGC("stop tracing")
|
||||
|
||||
// See the comment in StartTrace.
|
||||
lock(&sched.sysmonlock)
|
||||
|
||||
// See the comment in StartTrace.
|
||||
lock(&trace.bufLock)
|
||||
|
||||
if !trace.enabled {
|
||||
unlock(&trace.bufLock)
|
||||
unlock(&sched.sysmonlock)
|
||||
startTheWorldGC()
|
||||
return
|
||||
}
|
||||
|
|
@ -320,6 +330,8 @@ func StopTrace() {
|
|||
trace.shutdown = true
|
||||
unlock(&trace.bufLock)
|
||||
|
||||
unlock(&sched.sysmonlock)
|
||||
|
||||
startTheWorldGC()
|
||||
|
||||
// The world is started but we've set trace.shutdown, so new tracing can't start.
|
||||
|
|
|
|||
|
|
@ -339,6 +339,26 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
|
|||
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
||||
if perm&S_IWRITE == 0 {
|
||||
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)
|
||||
return h, e
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ type common struct {
|
|||
tempDirOnce sync.Once
|
||||
tempDir string
|
||||
tempDirErr error
|
||||
tempDirSeq int32
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
// 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 {
|
||||
// Use a single parent directory for all the temporary directories
|
||||
// created by a test, each numbered sequentially.
|
||||
c.tempDirOnce.Do(func() {
|
||||
c.Helper()
|
||||
|
||||
|
|
@ -849,7 +851,12 @@ func (c *common) TempDir() string {
|
|||
if c.tempDirErr != nil {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package testing_test
|
|||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -55,8 +56,11 @@ func testTempDir(t *testing.T) {
|
|||
t.Fatal("expected dir")
|
||||
}
|
||||
dir2 := t.TempDir()
|
||||
if dir != dir2 {
|
||||
t.Fatal("directory changed between calls")
|
||||
if dir == dir2 {
|
||||
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
|
||||
fi, err := os.Stat(dir)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue