summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-12-05 11:18:10 -0500
committerRuss Cox <rsc@golang.org>2014-12-05 11:18:10 -0500
commit829b286f2c5dc450394c526cd91ba8c8bea9bac8 (patch)
treeec666ce5582986e19a6b50467ceaac13763f2602
parente04c8b063fd7d7aaded8e1ff549dbb520038c61e (diff)
parent41c6b84342b27353ed40aa3942ef8647631a322b (diff)
downloadgo-git-829b286f2c5dc450394c526cd91ba8c8bea9bac8.tar.gz
[dev.cc] all: merge default (8d42099cdc23) into dev.cc
TBR=austin CC=golang-codereviews https://golang.org/cl/178700044
-rw-r--r--.hgtags1
-rw-r--r--doc/contrib.html21
-rw-r--r--doc/go_spec.html2
-rwxr-xr-xlib/time/update.bash4
-rw-r--r--lib/time/zoneinfo.zipbin358933 -> 360713 bytes
-rw-r--r--src/cmd/go/doc.go28
-rw-r--r--src/cmd/go/generate.go75
-rw-r--r--src/cmd/go/generate_test.go2
-rw-r--r--src/cmd/pprof/internal/commands/commands.go26
-rw-r--r--src/cmd/pprof/internal/symbolizer/symbolizer.go4
-rw-r--r--src/go/build/build.go8
-rw-r--r--src/go/build/build_test.go1
-rw-r--r--src/reflect/all_test.go101
-rw-r--r--src/reflect/export_test.go19
-rw-r--r--src/reflect/type.go4
-rw-r--r--src/runtime/proc1.go23
-rw-r--r--src/runtime/stack1.go8
17 files changed, 269 insertions, 58 deletions
diff --git a/.hgtags b/.hgtags
index 9867bc339f..eb9e82e408 100644
--- a/.hgtags
+++ b/.hgtags
@@ -137,3 +137,4 @@ f44017549ff9c3cc5eef74ebe7276cd0dfc066b6 go1.3.3
f44017549ff9c3cc5eef74ebe7276cd0dfc066b6 release
1fdfd7dfaedb1b7702141617e621ab7328a236a1 go1.4beta1
bffdd0cae380ce1ccf3e98ed6b6cd53fece7ba72 go1.4rc1
+6c4e66ae713704840fcea78c8055b44ba86ae5ea go1.4rc2
diff --git a/doc/contrib.html b/doc/contrib.html
index a615fc67a3..8a674d647c 100644
--- a/doc/contrib.html
+++ b/doc/contrib.html
@@ -30,21 +30,16 @@ We encourage all Go users to subscribe to
<h2 id="go1">Version history</h2>
<h3 id="release"><a href="/doc/devel/release.html">Release History</a></h3>
-<p>A summary of the changes between Go releases.</p>
-<h4 id="go1notes"><a href="/doc/go1">Go 1 Release Notes</a></h4>
-<p>
-A guide for updating your code to work with Go 1.
-</p>
+<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
-<h4 id="release notes"><a href="/doc/go1.1">Go 1.1 Release Notes</a></h4>
-<p>
-A list of significant changes in Go 1.1, with instructions for updating
-your code where necessary.
-Each point release includes a similar document appropriate for that
-release: <a href="/doc/go1.2">Go 1.2</a>, <a href="/doc/go1.3">Go 1.3</a>,
-and so on.
-</p>
+<ul>
+ <li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
+ <li><a href="/doc/go1.3">Go 1.3</a> <small>(June 2014)</small></li>
+ <li><a href="/doc/go1.2">Go 1.2</a> <small>(December 2013)</small></li>
+ <li><a href="/doc/go1.1">Go 1.1</a> <small>(May 2013)</small></li>
+ <li><a href="/doc/go1">Go 1</a> <small>(March 2012)</small></li>
+</ul>
<h3 id="go1compat"><a href="/doc/go1compat">Go 1 and the Future of Go Programs</a></h3>
<p>
diff --git a/doc/go_spec.html b/doc/go_spec.html
index ca0deb56a3..3b67f307f6 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -5579,7 +5579,7 @@ s3 := append(s2, s0...) // append a slice s3 == []int{0,
s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
var t []interface{}
-t = append(t, 42, 3.1415, "foo") t == []interface{}{42, 3.1415, "foo"}
+t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"}
var b []byte
b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
diff --git a/lib/time/update.bash b/lib/time/update.bash
index 8e1662afde..caa8450fa6 100755
--- a/lib/time/update.bash
+++ b/lib/time/update.bash
@@ -7,8 +7,8 @@
# downloaded from the ICANN/IANA distribution.
# Versions to use.
-CODE=2014d
-DATA=2014d
+CODE=2014j
+DATA=2014j
set -e
rm -rf work
diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip
index e0d3afe074..425d7c98fa 100644
--- a/lib/time/zoneinfo.zip
+++ b/lib/time/zoneinfo.zip
Binary files differ
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 43a3159440..65640fb483 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -234,17 +234,24 @@ create or update Go source files, for instance by running yacc.
Go generate is never run automatically by go build, go get, go test,
and so on. It must be run explicitly.
-Directives are written as a whole-line comment of the form
+Go generate scans the file for directives, which are lines of
+the form,
//go:generate command argument...
-(note: no space in "//go") where command is the generator to be
-run, corresponding to an executable file that can be run locally.
-It must either be in the shell path (gofmt), a fully qualified path
-(/usr/you/bin/mytool), or a command alias, described below.
+(note: no leading spaces and no space in "//go") where command
+is the generator to be run, corresponding to an executable file
+that can be run locally. It must either be in the shell path
+(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
+command alias, described below.
-The arguments are space-separated tokens or double-quoted strings
-passed to the generator as individual arguments when it is run.
+Note that go generate does not parse the file, so lines that look
+like directives in comments or multiline strings will be treated
+as directives.
+
+The arguments to the directive are space-separated tokens or
+double-quoted strings passed to the generator as individual
+arguments when it is run.
Quoted strings use Go syntax and are evaluated before execution; a
quoted string appears as a single argument to the generator.
@@ -317,7 +324,7 @@ Download and install packages and dependencies
Usage:
- go get [-d] [-fix] [-t] [-u] [build flags] [packages]
+ go get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]
Get downloads and installs the packages named by the import paths,
along with their dependencies.
@@ -325,6 +332,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
+The -f flag, valid only when -u is set, forces get -u not to verify that
+each package has been checked out from the source control repository
+implied by its import path. This can be useful if the source is a local fork
+of the original.
+
The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go
index a83cce8f7a..baf4d2b55c 100644
--- a/src/cmd/go/generate.go
+++ b/src/cmd/go/generate.go
@@ -32,20 +32,27 @@ create or update Go source files, for instance by running yacc.
Go generate is never run automatically by go build, go get, go test,
and so on. It must be run explicitly.
-Directives are written as a whole-line comment of the form
+Go generate scans the file for directives, which are lines of
+the form,
//go:generate command argument...
-(note: no space in "//go") where command is the generator to be
-run, corresponding to an executable file that can be run locally.
-It must either be in the shell path (gofmt), a fully qualified path
-(/usr/you/bin/mytool), or a command alias, described below.
+(note: no leading spaces and no space in "//go") where command
+is the generator to be run, corresponding to an executable file
+that can be run locally. It must either be in the shell path
+(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
+command alias, described below.
-The arguments are space-separated tokens or double-quoted strings
-passed to the generator as individual arguments when it is run.
+Note that go generate does not parse the file, so lines that look
+like directives in comments or multiline strings will be treated
+as directives.
+
+The arguments to the directive are space-separated tokens or
+double-quoted strings passed to the generator as individual
+arguments when it is run.
Quoted strings use Go syntax and are evaluated before execution; a
-quoted string appears a single argument to the generator.
+quoted string appears as a single argument to the generator.
Go generate sets several variables when it runs the generator:
@@ -178,13 +185,43 @@ func (g *Generator) run() (ok bool) {
fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path))
}
- s := bufio.NewScanner(g.r)
- for s.Scan() {
- g.lineNum++
- if !bytes.HasPrefix(s.Bytes(), []byte("//go:generate ")) && !bytes.HasPrefix(s.Bytes(), []byte("//go:generate\t")) {
+ // Scan for lines that start "//go:generate".
+ // Can't use bufio.Scanner because it can't handle long lines,
+ // which are likely to appear when using generate.
+ input := bufio.NewReader(g.r)
+ var err error
+ // One line per loop.
+ for {
+ g.lineNum++ // 1-indexed.
+ var buf []byte
+ buf, err = input.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ // Line too long - consume and ignore.
+ if isGoGenerate(buf) {
+ g.errorf("directive too long")
+ }
+ for err == bufio.ErrBufferFull {
+ _, err = input.ReadSlice('\n')
+ }
+ if err != nil {
+ break
+ }
+ continue
+ }
+
+ if err != nil {
+ // Check for marker at EOF without final \n.
+ if err == io.EOF && isGoGenerate(buf) {
+ err = io.ErrUnexpectedEOF
+ }
+ break
+ }
+
+ if !isGoGenerate(buf) {
continue
}
- words := g.split(s.Text())
+
+ words := g.split(string(buf))
if len(words) == 0 {
g.errorf("no arguments to directive")
}
@@ -201,19 +238,23 @@ func (g *Generator) run() (ok bool) {
}
g.exec(words)
}
- if s.Err() != nil {
- g.errorf("error reading %s: %s", shortPath(g.path), s.Err())
+ if err != nil && err != io.EOF {
+ g.errorf("error reading %s: %s", shortPath(g.path), err)
}
return true
}
+func isGoGenerate(buf []byte) bool {
+ return bytes.HasPrefix(buf, []byte("//go:generate ")) || bytes.HasPrefix(buf, []byte("//go:generate\t"))
+}
+
// split breaks the line into words, evaluating quoted
// strings and evaluating environment variables.
-// The initial //go:generate element is dropped.
+// The initial //go:generate element is present in line.
func (g *Generator) split(line string) []string {
// Parse line, obeying quoted strings.
var words []string
- line = line[len("//go:generate "):]
+ line = line[len("//go:generate ") : len(line)-1] // Drop preamble and final newline.
// One (possibly quoted) word per iteration.
Words:
for {
diff --git a/src/cmd/go/generate_test.go b/src/cmd/go/generate_test.go
index 93c0ae66e9..660ebabbe8 100644
--- a/src/cmd/go/generate_test.go
+++ b/src/cmd/go/generate_test.go
@@ -40,7 +40,7 @@ func TestGenerateCommandParse(t *testing.T) {
}
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
for _, test := range splitTests {
- got := g.split("//go:generate " + test.in)
+ got := g.split("//go:generate " + test.in + "\n")
if !reflect.DeepEqual(got, test.out) {
t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
}
diff --git a/src/cmd/pprof/internal/commands/commands.go b/src/cmd/pprof/internal/commands/commands.go
index 6d35820184..51397a3c60 100644
--- a/src/cmd/pprof/internal/commands/commands.go
+++ b/src/cmd/pprof/internal/commands/commands.go
@@ -11,6 +11,7 @@ import (
"io"
"os"
"os/exec"
+ "runtime"
"strings"
"cmd/pprof/internal/plugin"
@@ -71,15 +72,27 @@ func PProf(c Completer, interactive **bool, svgpan **string) Commands {
"eog": {c, report.Dot, invokeVisualizer(interactive, invokeDot("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"},
"evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"},
"gv": {c, report.Dot, invokeVisualizer(interactive, invokeDot("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"},
- "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(svgpan), "svg", browsers), false, "Visualize graph through web browser"},
+ "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(svgpan), "svg", browsers()), false, "Visualize graph through web browser"},
// Visualize HTML directly generated by report.
- "weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers), true, "Output annotated source in HTML for functions matching regexp or address"},
+ "weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"},
}
}
-// List of web browsers to attempt for web visualization
-var browsers = []string{"chrome", "google-chrome", "firefox", "/usr/bin/open"}
+// browsers returns a list of commands to attempt for web visualization
+// on the current platform
+func browsers() []string {
+ cmds := []string{"chrome", "google-chrome", "firefox"}
+ switch runtime.GOOS {
+ case "darwin":
+ cmds = append(cmds, "/usr/bin/open")
+ case "windows":
+ cmds = append(cmds, "cmd /c start")
+ default:
+ cmds = append(cmds, "xdg-open")
+ }
+ return cmds
+}
// NewCompleter creates an autocompletion function for a set of commands.
func NewCompleter(cs Commands) Completer {
@@ -142,6 +155,10 @@ func awayFromTTY(format string) PostProcessor {
func invokeDot(format string) PostProcessor {
divert := awayFromTTY(format)
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
+ if _, err := exec.LookPath("dot"); err != nil {
+ ui.PrintErr("Cannot find dot, have you installed Graphviz?")
+ return err
+ }
cmd := exec.Command("dot", "-T"+format)
var buf bytes.Buffer
cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
@@ -174,6 +191,7 @@ func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, v
if err = format(input, tempFile, ui); err != nil {
return err
}
+ tempFile.Close() // on windows, if the file is Open, start cannot access it.
// Try visualizers until one is successful
for _, v := range visualizers {
// Separate command and arguments for exec.Command.
diff --git a/src/cmd/pprof/internal/symbolizer/symbolizer.go b/src/cmd/pprof/internal/symbolizer/symbolizer.go
index cabddaa76e..86de5640d2 100644
--- a/src/cmd/pprof/internal/symbolizer/symbolizer.go
+++ b/src/cmd/pprof/internal/symbolizer/symbolizer.go
@@ -32,6 +32,10 @@ func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin
}
}
+ if len(prof.Mapping) == 0 {
+ return fmt.Errorf("no known mappings")
+ }
+
mt, err := newMapping(prof, obj, ui, force)
if err != nil {
return err
diff --git a/src/go/build/build.go b/src/go/build/build.go
index ed79df2939..ab2976756b 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -1310,11 +1310,13 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
// auto-tagging to apply only to files with a non-empty prefix, so
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
// sytems, such as android, to arrive without breaking existing code with
- // innocuous source code in "android.go". The easiest fix: files without
- // underscores are always included.
- if !strings.ContainsRune(name, '_') {
+ // innocuous source code in "android.go". The easiest fix: cut everything
+ // in the name before the initial _.
+ i := strings.Index(name, "_")
+ if i < 0 {
return true
}
+ name = name[i:] // ignore everything before first _
l := strings.Split(name, "_")
if n := len(l); n > 0 && l[n-1] == "test" {
diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go
index 43d09cbd14..a40def0fa0 100644
--- a/src/go/build/build_test.go
+++ b/src/go/build/build_test.go
@@ -189,6 +189,7 @@ var matchFileTests = []struct {
{ctxtAndroid, "foo_plan9.go", "", false},
{ctxtAndroid, "android.go", "", true},
{ctxtAndroid, "plan9.go", "", true},
+ {ctxtAndroid, "plan9_test.go", "", true},
{ctxtAndroid, "arm.s", "", true},
{ctxtAndroid, "amd64.s", "", true},
}
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index c62f227f43..9d092d9acf 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4060,3 +4060,104 @@ func TestLargeGCProg(t *testing.T) {
fv := ValueOf(func([256]*byte) {})
fv.Call([]Value{ValueOf([256]*byte{})})
}
+
+// Issue 9179.
+func TestCallGC(t *testing.T) {
+ f := func(a, b, c, d, e string) {
+ }
+ g := func(in []Value) []Value {
+ runtime.GC()
+ return nil
+ }
+ typ := ValueOf(f).Type()
+ f2 := MakeFunc(typ, g).Interface().(func(string, string, string, string, string))
+ f2("four", "five5", "six666", "seven77", "eight888")
+}
+
+type funcLayoutTest struct {
+ rcvr, t Type
+ argsize, retOffset uintptr
+ stack []byte
+}
+
+var funcLayoutTests []funcLayoutTest
+
+func init() {
+ var argAlign = PtrSize
+ if runtime.GOARCH == "amd64p32" {
+ argAlign = 2 * PtrSize
+ }
+ roundup := func(x uintptr, a uintptr) uintptr {
+ return (x + a - 1) / a * a
+ }
+
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a, b string) string { return "" }).Type(),
+ 4 * PtrSize,
+ 4 * PtrSize,
+ []byte{BitsPointer, BitsScalar, BitsPointer},
+ })
+
+ var r []byte
+ if PtrSize == 4 {
+ r = []byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer}
+ } else {
+ r = []byte{BitsScalar, BitsScalar, BitsPointer}
+ }
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(),
+ roundup(3*4, PtrSize) + PtrSize + 2,
+ roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
+ r,
+ })
+
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(),
+ 4 * PtrSize,
+ 4 * PtrSize,
+ []byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer},
+ })
+
+ type S struct {
+ a, b uintptr
+ c, d *byte
+ }
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a S) {}).Type(),
+ 4 * PtrSize,
+ 4 * PtrSize,
+ []byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer},
+ })
+
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ ValueOf((*byte)(nil)).Type(),
+ ValueOf(func(a uintptr, b *int) {}).Type(),
+ 3 * PtrSize,
+ roundup(3*PtrSize, argAlign),
+ []byte{BitsPointer, BitsScalar, BitsPointer},
+ })
+}
+
+func TestFuncLayout(t *testing.T) {
+ for _, lt := range funcLayoutTests {
+ _, argsize, retOffset, stack := FuncLayout(lt.t, lt.rcvr)
+ if argsize != lt.argsize {
+ t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize)
+ }
+ if retOffset != lt.retOffset {
+ t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset)
+ }
+ if !bytes.Equal(stack, lt.stack) {
+ t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack)
+ }
+ }
+}
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index 0778ad37f5..caaf51a50f 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -17,3 +17,22 @@ func IsRO(v Value) bool {
var ArrayOf = arrayOf
var CallGC = &callGC
+
+const PtrSize = ptrSize
+const BitsPointer = bitsPointer
+const BitsScalar = bitsScalar
+
+func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte) {
+ var ft *rtype
+ var s *bitVector
+ if rcvr != nil {
+ ft, argSize, retOffset, s = funcLayout(t.(*rtype), rcvr.(*rtype))
+ } else {
+ ft, argSize, retOffset, s = funcLayout(t.(*rtype), nil)
+ }
+ frametype = ft
+ for i := uint32(0); i < s.n; i += 2 {
+ stack = append(stack, s.data[i/8]>>(i%8)&3)
+ }
+ return
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index e05a3f9d17..441459b3f5 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1889,14 +1889,14 @@ func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) {
switch Kind(t.kind & kindMask) {
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
// 1 pointer at start of representation
- for bv.n < uint32(*offset/uintptr(ptrSize)) {
+ for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
bv.append2(bitsScalar)
}
bv.append2(bitsPointer)
case Interface:
// 2 pointers
- for bv.n < uint32(*offset/uintptr(ptrSize)) {
+ for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
bv.append2(bitsScalar)
}
bv.append2(bitsPointer)
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 81b211d0d3..aeded0e773 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -371,6 +371,11 @@ func casgstatus(gp *g, oldval, newval uint32) {
// loop if gp->atomicstatus is in a scan state giving
// GC time to finish and change the state to oldval.
for !cas(&gp.atomicstatus, oldval, newval) {
+ if oldval == _Gwaiting && gp.atomicstatus == _Grunnable {
+ systemstack(func() {
+ gothrow("casgstatus: waiting for Gwaiting but is Grunnable")
+ })
+ }
// Help GC if needed.
if gp.preemptscan && !gp.gcworkdone && (oldval == _Grunning || oldval == _Gsyscall) {
gp.preemptscan = false
@@ -381,6 +386,24 @@ func casgstatus(gp *g, oldval, newval uint32) {
}
}
+// casgstatus(gp, oldstatus, Gcopystack), assuming oldstatus is Gwaiting or Grunnable.
+// Returns old status. Cannot call casgstatus directly, because we are racing with an
+// async wakeup that might come in from netpoll. If we see Gwaiting from the readgstatus,
+// it might have become Grunnable by the time we get to the cas. If we called casgstatus,
+// it would loop waiting for the status to go back to Gwaiting, which it never will.
+//go:nosplit
+func casgcopystack(gp *g) uint32 {
+ for {
+ oldstatus := readgstatus(gp) &^ _Gscan
+ if oldstatus != _Gwaiting && oldstatus != _Grunnable {
+ gothrow("copystack: bad status, not Gwaiting or Grunnable")
+ }
+ if cas(&gp.atomicstatus, oldstatus, _Gcopystack) {
+ return oldstatus
+ }
+ }
+}
+
// stopg ensures that gp is stopped at a GC safe point where its stack can be scanned
// or in the context of a moving collector the pointers can be flipped from pointing
// to old object to pointing to new objects.
diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index ad83e58951..1fd61ce1a2 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -563,13 +563,7 @@ func copystack(gp *g, newsize uintptr) {
}
memmove(unsafe.Pointer(new.hi-used), unsafe.Pointer(old.hi-used), used)
- oldstatus := readgstatus(gp)
- oldstatus &^= _Gscan
- if oldstatus == _Gwaiting || oldstatus == _Grunnable {
- casgstatus(gp, oldstatus, _Gcopystack) // oldstatus is Gwaiting or Grunnable
- } else {
- gothrow("copystack: bad status, not Gwaiting or Grunnable")
- }
+ oldstatus := casgcopystack(gp) // cas from Gwaiting or Grunnable to Gcopystack, return old status
// Swap out old stack for new one
gp.stack = new