libgo: update to Go 1.14.4 release
authorIan Lance Taylor <iant@golang.org>
Fri, 10 Jul 2020 17:51:40 +0000 (10:51 -0700)
committerIan Lance Taylor <iant@golang.org>
Fri, 10 Jul 2020 18:30:23 +0000 (11:30 -0700)
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/241999

23 files changed:
gcc/go/gofrontend/MERGE
libgo/MERGE
libgo/VERSION
libgo/go/cmd/cgo/gcc.go
libgo/go/encoding/json/decode.go
libgo/go/encoding/json/decode_test.go
libgo/go/encoding/json/encode.go
libgo/go/encoding/json/encode_test.go
libgo/go/encoding/json/stream_test.go
libgo/go/go/doc/example.go
libgo/go/go/doc/example_test.go
libgo/go/go/parser/interface.go
libgo/go/math/big/nat.go
libgo/go/math/big/nat_test.go
libgo/go/os/os_test.go
libgo/go/runtime/crash_test.go
libgo/go/runtime/mgcscavenge.go
libgo/go/runtime/mpagecache.go
libgo/go/runtime/mpagecache_test.go
libgo/go/runtime/proc.go
libgo/go/runtime/proc_test.go
libgo/go/runtime/testdata/testprog/lockosthread.go
libgo/misc/cgo/test/testx.go

index ecef60400cc1a188591f56219cd03650686929b4..64b13f410e083bb46dfdd85b48d14d902506cb07 100644 (file)
@@ -1,4 +1,4 @@
-30674246ef60ab74566a21f362a7de7a09b99955
+2ad0970e9da95024110cd3244e9e21313af70a5f
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 8cae45f6b8eb875cb41c4c1f037b2cd863305049..07547d064a1c40798fd68d70a2e161d360c346c1 100644 (file)
@@ -1,4 +1,4 @@
-96745b980cfde139e8611772e2bc0c59a8e6cdf7
+83b181c68bf332ac7948f145f33d128377a09c42
 
 The first line of this file holds the git revision number of the
 last merge done from the master library sources.
index 864916ef4996134e87bb7be0070c1da86fd0dece..d8281a2e9968ae9f816d1e099032b14f43ca0736 100644 (file)
@@ -1 +1 @@
-go1.14.2
+go1.14.4
index 310316bdda495196c02ba9fa174d336606fe4806..e38972913be2df18324e6704b85e0807d3fbb0c1 100644 (file)
@@ -2082,6 +2082,10 @@ var goIdent = make(map[string]*ast.Ident)
 // that may contain a pointer. This is used for cgo pointer checking.
 var unionWithPointer = make(map[ast.Expr]bool)
 
+// anonymousStructTag provides a consistent tag for an anonymous struct.
+// The same dwarf.StructType pointer will always get the same tag.
+var anonymousStructTag = make(map[*dwarf.StructType]string)
+
 func (c *typeConv) Init(ptrSize, intSize int64) {
        c.ptrSize = ptrSize
        c.intSize = intSize
@@ -2430,8 +2434,12 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
                        break
                }
                if tag == "" {
-                       tag = "__" + strconv.Itoa(tagGen)
-                       tagGen++
+                       tag = anonymousStructTag[dt]
+                       if tag == "" {
+                               tag = "__" + strconv.Itoa(tagGen)
+                               tagGen++
+                               anonymousStructTag[dt] = tag
+                       }
                } else if t.C.Empty() {
                        t.C.Set(dt.Kind + " " + tag)
                }
index b43484692e18565d453fa7c8b364435bf7e56803..b60e2bb0b2c4dbee63f3a4d7e59c7cb03550abe3 100644 (file)
@@ -1217,6 +1217,11 @@ func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) {
        if r == -1 {
                return s, true
        }
+       // Only perform up to one safe unquote for each re-scanned string
+       // literal. In some edge cases, the decoder unquotes a literal a second
+       // time, even after another literal has been re-scanned. Thus, only the
+       // first unquote can safely use safeUnquote.
+       d.safeUnquote = 0
 
        b := make([]byte, len(s)+2*utf8.UTFMax)
        w := copy(b, s[0:r])
index 498bd97b46ef41dc9a2ceba27d449be428fc6d30..a49181e9823d378e1eac372281722981678e3ad7 100644 (file)
@@ -2419,7 +2419,7 @@ func (m *textUnmarshalerString) UnmarshalText(text []byte) error {
        return nil
 }
 
-// Test unmarshal to a map, with map key is a user defined type.
+// Test unmarshal to a map, where the map key is a user defined type.
 // See golang.org/issues/34437.
 func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
        var p map[textUnmarshalerString]string
@@ -2428,6 +2428,35 @@ func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
        }
 
        if _, ok := p["foo"]; !ok {
-               t.Errorf(`Key "foo" is not existed in map: %v`, p)
+               t.Errorf(`Key "foo" does not exist in map: %v`, p)
+       }
+}
+
+func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
+       // See golang.org/issues/38105.
+       var p map[textUnmarshalerString]string
+       if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
+               t.Fatalf("Unmarshal unexpected error: %v", err)
+       }
+       if _, ok := p["开源"]; !ok {
+               t.Errorf(`Key "开源" does not exist in map: %v`, p)
+       }
+
+       // See golang.org/issues/38126.
+       type T struct {
+               F1 string `json:"F1,string"`
+       }
+       t1 := T{"aaa\tbbb"}
+
+       b, err := Marshal(t1)
+       if err != nil {
+               t.Fatalf("Marshal unexpected error: %v", err)
+       }
+       var t2 T
+       if err := Unmarshal(b, &t2); err != nil {
+               t.Fatalf("Unmarshal unexpected error: %v", err)
+       }
+       if t1 != t2 {
+               t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
        }
 }
index 39cdaebde7b9c94e37ffec3f3c59a16ac8138382..b351cf3f4499acc029a824270d096915bda58c1f 100644 (file)
@@ -635,11 +635,12 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
                return
        }
        if opts.quoted {
-               b := make([]byte, 0, v.Len()+2)
-               b = append(b, '"')
-               b = append(b, []byte(v.String())...)
-               b = append(b, '"')
-               e.stringBytes(b, opts.escapeHTML)
+               e2 := newEncodeState()
+               // Since we encode the string twice, we only need to escape HTML
+               // the first time.
+               e2.string(v.String(), opts.escapeHTML)
+               e.stringBytes(e2.Bytes(), false)
+               encodeStatePool.Put(e2)
        } else {
                e.string(v.String(), opts.escapeHTML)
        }
index 5110c7de9b123387aaf5cee9bb2e71b046c94276..7290eca06f070769bf46225123ebde9c728294d6 100644 (file)
@@ -79,37 +79,66 @@ type StringTag struct {
        NumberStr  Number  `json:",string"`
 }
 
-var stringTagExpected = `{
- "BoolStr": "true",
- "IntStr": "42",
- "UintptrStr": "44",
- "StrStr": "\"xzbit\"",
- "NumberStr": "46"
-}`
-
-func TestStringTag(t *testing.T) {
-       var s StringTag
-       s.BoolStr = true
-       s.IntStr = 42
-       s.UintptrStr = 44
-       s.StrStr = "xzbit"
-       s.NumberStr = "46"
-       got, err := MarshalIndent(&s, "", " ")
-       if err != nil {
-               t.Fatal(err)
-       }
-       if got := string(got); got != stringTagExpected {
-               t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected)
+func TestRoundtripStringTag(t *testing.T) {
+       tests := []struct {
+               name string
+               in   StringTag
+               want string // empty to just test that we roundtrip
+       }{
+               {
+                       name: "AllTypes",
+                       in: StringTag{
+                               BoolStr:    true,
+                               IntStr:     42,
+                               UintptrStr: 44,
+                               StrStr:     "xzbit",
+                               NumberStr:  "46",
+                       },
+                       want: `{
+                               "BoolStr": "true",
+                               "IntStr": "42",
+                               "UintptrStr": "44",
+                               "StrStr": "\"xzbit\"",
+                               "NumberStr": "46"
+                       }`,
+               },
+               {
+                       // See golang.org/issues/38173.
+                       name: "StringDoubleEscapes",
+                       in: StringTag{
+                               StrStr:    "\b\f\n\r\t\"\\",
+                               NumberStr: "0", // just to satisfy the roundtrip
+                       },
+                       want: `{
+                               "BoolStr": "false",
+                               "IntStr": "0",
+                               "UintptrStr": "0",
+                               "StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"",
+                               "NumberStr": "0"
+                       }`,
+               },
        }
+       for _, test := range tests {
+               t.Run(test.name, func(t *testing.T) {
+                       // Indent with a tab prefix to make the multi-line string
+                       // literals in the table nicer to read.
+                       got, err := MarshalIndent(&test.in, "\t\t\t", "\t")
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if got := string(got); got != test.want {
+                               t.Fatalf(" got: %s\nwant: %s\n", got, test.want)
+                       }
 
-       // Verify that it round-trips.
-       var s2 StringTag
-       err = NewDecoder(bytes.NewReader(got)).Decode(&s2)
-       if err != nil {
-               t.Fatalf("Decode: %v", err)
-       }
-       if !reflect.DeepEqual(s, s2) {
-               t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2)
+                       // Verify that it round-trips.
+                       var s2 StringTag
+                       if err := Unmarshal(got, &s2); err != nil {
+                               t.Fatalf("Decode: %v", err)
+                       }
+                       if !reflect.DeepEqual(test.in, s2) {
+                               t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
+                       }
+               })
        }
 }
 
index ebb4f231d151892c981f495470825dee12f8cf57..c9e5334337dff035de49c6621a5aa2ad9c6124b2 100644 (file)
@@ -144,14 +144,15 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
                },
                {
                        "stringOption", stringOption,
-                       `{"bar":"\"\u003chtml\u003efoobar\u003c/html\u003e\""}`,
+                       `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
                        `{"bar":"\"<html>foobar</html>\""}`,
                },
        } {
                var buf bytes.Buffer
                enc := NewEncoder(&buf)
                if err := enc.Encode(tt.v); err != nil {
-                       t.Fatalf("Encode(%s): %s", tt.name, err)
+                       t.Errorf("Encode(%s): %s", tt.name, err)
+                       continue
                }
                if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
                        t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
@@ -159,7 +160,8 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
                buf.Reset()
                enc.SetEscapeHTML(false)
                if err := enc.Encode(tt.v); err != nil {
-                       t.Fatalf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
+                       t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
+                       continue
                }
                if got := strings.TrimSpace(buf.String()); got != tt.want {
                        t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
index a010d3a85ab588a324db1dc0b1b6a010b755ee41..ebf81189b5b283732b39cdb8de483416e602312f 100644 (file)
@@ -62,9 +62,6 @@ func Examples(testFiles ...*ast.File) []*Example {
                        if !ok || f.Recv != nil {
                                continue
                        }
-                       if params := f.Type.Params; len(params.List) != 0 {
-                               continue // function has params; not a valid example
-                       }
                        numDecl++
                        name := f.Name.Name
                        if isTest(name, "Test") || isTest(name, "Benchmark") {
@@ -74,6 +71,9 @@ func Examples(testFiles ...*ast.File) []*Example {
                        if !isTest(name, "Example") {
                                continue
                        }
+                       if params := f.Type.Params; len(params.List) != 0 {
+                               continue // function has params; not a valid example
+                       }
                        if f.Body == nil { // ast.File.Body nil dereference (see issue 28044)
                                continue
                        }
index cd2f469c2fbc531c2a5a2e3abf00bbcbb5a1535b..32db3cd7da7b6c702d179d3debb32fc0945758e3 100644 (file)
@@ -331,25 +331,65 @@ func main() {
 }
 `
 
+const exampleWholeFileFunction = `package foo_test
+
+func Foo(x int) {
+}
+
+func Example() {
+       fmt.Println("Hello, world!")
+       // Output: Hello, world!
+}
+`
+
+const exampleWholeFileFunctionOutput = `package main
+
+func Foo(x int) {
+}
+
+func main() {
+       fmt.Println("Hello, world!")
+}
+`
+
+var exampleWholeFileTestCases = []struct {
+       Title, Source, Play, Output string
+}{
+       {
+               "Methods",
+               exampleWholeFile,
+               exampleWholeFileOutput,
+               "Hello, world!\n",
+       },
+       {
+               "Function",
+               exampleWholeFileFunction,
+               exampleWholeFileFunctionOutput,
+               "Hello, world!\n",
+       },
+}
+
 func TestExamplesWholeFile(t *testing.T) {
-       fset := token.NewFileSet()
-       file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleWholeFile), parser.ParseComments)
-       if err != nil {
-               t.Fatal(err)
-       }
-       es := doc.Examples(file)
-       if len(es) != 1 {
-               t.Fatalf("wrong number of examples; got %d want 1", len(es))
-       }
-       e := es[0]
-       if e.Name != "" {
-               t.Errorf("got Name == %q, want %q", e.Name, "")
-       }
-       if g, w := formatFile(t, fset, e.Play), exampleWholeFileOutput; g != w {
-               t.Errorf("got Play == %q, want %q", g, w)
-       }
-       if g, w := e.Output, "Hello, world!\n"; g != w {
-               t.Errorf("got Output == %q, want %q", g, w)
+       for _, c := range exampleWholeFileTestCases {
+               fset := token.NewFileSet()
+               file, err := parser.ParseFile(fset, "test.go", strings.NewReader(c.Source), parser.ParseComments)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               es := doc.Examples(file)
+               if len(es) != 1 {
+                       t.Fatalf("%s: wrong number of examples; got %d want 1", c.Title, len(es))
+               }
+               e := es[0]
+               if e.Name != "" {
+                       t.Errorf("%s: got Name == %q, want %q", c.Title, e.Name, "")
+               }
+               if g, w := formatFile(t, fset, e.Play), c.Play; g != w {
+                       t.Errorf("%s: got Play == %q, want %q", c.Title, g, w)
+               }
+               if g, w := e.Output, c.Output; g != w {
+                       t.Errorf("%s: got Output == %q, want %q", c.Title, g, w)
+               }
        }
 }
 
index 500c98d49669ca97c590726ba2fb885a4b0e4400..54f9d7b80ac9a6a1c364514c498866694398db0c 100644 (file)
@@ -133,13 +133,7 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
 // first error encountered are returned.
 //
 func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
-       fd, err := os.Open(path)
-       if err != nil {
-               return nil, err
-       }
-       defer fd.Close()
-
-       list, err := fd.Readdir(-1)
+       list, err := ioutil.ReadDir(path)
        if err != nil {
                return nil, err
        }
index 1b771ca7c6357f08acbec12d9c284728bf4747cb..c31ec5156b81d8830b189245be2b399c6f8d6e2b 100644 (file)
@@ -740,7 +740,8 @@ func (z nat) divLarge(u, uIn, vIn nat) (q, r nat) {
 // The remainder overwrites input u.
 //
 // Precondition:
-// - len(q) >= len(u)-len(v)
+// - q is large enough to hold the quotient u / v
+//   which has a maximum length of len(u)-len(v)+1.
 func (q nat) divBasic(u, v nat) {
        n := len(v)
        m := len(u) - n
@@ -779,6 +780,8 @@ func (q nat) divBasic(u, v nat) {
                }
 
                // D4.
+               // Compute the remainder u - (q̂*v) << (_W*j).
+               // The subtraction may overflow if q̂ estimate was off by one.
                qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0)
                qhl := len(qhatv)
                if j+qhl > len(u) && qhatv[n] == 0 {
@@ -787,7 +790,11 @@ func (q nat) divBasic(u, v nat) {
                c := subVV(u[j:j+qhl], u[j:], qhatv)
                if c != 0 {
                        c := addVV(u[j:j+n], u[j:], v)
-                       u[j+n] += c
+                       // If n == qhl, the carry from subVV and the carry from addVV
+                       // cancel out and don't affect u[j+n].
+                       if n < qhl {
+                               u[j+n] += c
+                       }
                        qhat--
                }
 
@@ -827,6 +834,10 @@ func (z nat) divRecursive(u, v nat) {
        putNat(tmp)
 }
 
+// divRecursiveStep computes the division of u by v.
+// - z must be large enough to hold the quotient
+// - the quotient will overwrite z
+// - the remainder will overwrite u
 func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) {
        u = u.norm()
        v = v.norm()
index 32f29e38769285271f718fc0f4f5b9026844ef84..89e913fc16ea4954737e316b657aea7a4aa500c1 100644 (file)
@@ -786,3 +786,21 @@ func TestNatDiv(t *testing.T) {
                }
        }
 }
+
+// TestIssue37499 triggers the edge case of divBasic where
+// the inaccurate estimate of the first word's quotient
+// happens at the very beginning of the loop.
+func TestIssue37499(t *testing.T) {
+       // Choose u and v such that v is slightly larger than u >> N.
+       // This tricks divBasic into choosing 1 as the first word
+       // of the quotient. This works in both 32-bit and 64-bit settings.
+       u := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706b39f8489c1d28e57bb5ba4ef9fd9387a3e344402c0a453381")
+       v := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706c")
+
+       q := nat(nil).make(8)
+       q.divBasic(u, v)
+       q = q.norm()
+       if s := string(q.utoa(16)); s != "fffffffffffffffffffffffffffffffffffffffffffffffb" {
+               t.Fatalf("incorrect quotient: %s", s)
+       }
+}
index a19b46d2964b06a1723cf5f2ea23b96ea58afb05..8ec6de7c07834496cc164bbe340d280e17e9f6ee 100644 (file)
@@ -2450,3 +2450,38 @@ func TestDirSeek(t *testing.T) {
                }
        }
 }
+
+// Test that opening a file does not change its permissions.  Issue 38225.
+func TestOpenFileKeepsPermissions(t *testing.T) {
+       t.Parallel()
+       dir, err := ioutil.TempDir("", "TestOpenFileKeepsPermissions")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer RemoveAll(dir)
+       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())
+       }
+}
index 6268f2ed72653f2a84e8af797e79eba0bb660394..aa97cf7d56822abe92917d63df69ed092d528f91 100644 (file)
@@ -55,6 +55,16 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
                t.Fatal(err)
        }
 
+       return runBuiltTestProg(t, exe, name, env...)
+}
+
+func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
+       if *flagQuick {
+               t.Skip("-quick")
+       }
+
+       testenv.MustHaveGoBuild(t)
+
        cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
        cmd.Env = append(cmd.Env, env...)
        if testing.Short() {
@@ -64,7 +74,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
        cmd.Stdout = &b
        cmd.Stderr = &b
        if err := cmd.Start(); err != nil {
-               t.Fatalf("starting %s %s: %v", binary, name, err)
+               t.Fatalf("starting %s %s: %v", exe, name, err)
        }
 
        // If the process doesn't complete within 1 minute,
@@ -92,7 +102,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
        }()
 
        if err := cmd.Wait(); err != nil {
-               t.Logf("%s %s exit status: %v", binary, name, err)
+               t.Logf("%s %s exit status: %v", exe, name, err)
        }
        close(done)
 
index 3b60b3d51fde8c8ebec00f7e6d3da6320a9fcf69..d4b527c0713deb43686db8ffb8eded2caaf68186 100644 (file)
@@ -288,6 +288,28 @@ func bgscavenge(c chan int) {
                        continue
                }
 
+               if released < physPageSize {
+                       // If this happens, it means that we may have attempted to release part
+                       // of a physical page, but the likely effect of that is that it released
+                       // the whole physical page, some of which may have still been in-use.
+                       // This could lead to memory corruption. Throw.
+                       throw("released less than one physical page of memory")
+               }
+
+               // On some platforms we may see crit as zero if the time it takes to scavenge
+               // memory is less than the minimum granularity of its clock (e.g. Windows).
+               // In this case, just assume scavenging takes 10 µs per regular physical page
+               // (determined empirically), and conservatively ignore the impact of huge pages
+               // on timing.
+               //
+               // We shouldn't ever see a crit value less than zero unless there's a bug of
+               // some kind, either on our side or in the platform we're running on, but be
+               // defensive in that case as well.
+               const approxCritNSPerPhysicalPage = 10e3
+               if crit <= 0 {
+                       crit = approxCritNSPerPhysicalPage * float64(released/physPageSize)
+               }
+
                // Multiply the critical time by 1 + the ratio of the costs of using
                // scavenged memory vs. scavenging memory. This forces us to pay down
                // the cost of reusing this memory eagerly by sleeping for a longer period
index 9fc338bd8e4f4ff7791fa337d75e40a4a2dce273..a074961840582800b89def8eba11c93205959709 100644 (file)
@@ -148,9 +148,14 @@ func (s *pageAlloc) allocToCache() pageCache {
        // Update as an allocation, but note that it's not contiguous.
        s.update(c.base, pageCachePages, false, true)
 
-       // We're always searching for the first free page, and we always know the
-       // up to pageCache size bits will be allocated, so we can always move the
-       // searchAddr past the cache.
-       s.searchAddr = c.base + pageSize*pageCachePages
+       // Set the search address to the last page represented by the cache.
+       // Since all of the pages in this block are going to the cache, and we
+       // searched for the first free page, we can confidently start at the
+       // next page.
+       //
+       // However, s.searchAddr is not allowed to point into unmapped heap memory
+       // unless it is maxSearchAddr, so make it the last page as opposed to
+       // the page after.
+       s.searchAddr = c.base + pageSize*(pageCachePages-1)
        return c
 }
index b8cc0bd965f5924941f4fb2041dc8a477ff0e7ad..2ed0c0aa6a0ba82da5c78a1601514120840b5b0b 100644 (file)
@@ -260,12 +260,13 @@ func TestPageAllocAllocToCache(t *testing.T) {
        if GOOS == "openbsd" && testing.Short() {
                t.Skip("skipping because virtual memory is limited; see #36210")
        }
-       tests := map[string]struct {
+       type test struct {
                before map[ChunkIdx][]BitRange
                scav   map[ChunkIdx][]BitRange
                hits   []PageCache // expected base addresses and patterns
                after  map[ChunkIdx][]BitRange
-       }{
+       }
+       tests := map[string]test{
                "AllFree": {
                        before: map[ChunkIdx][]BitRange{
                                BaseChunkIdx: {},
@@ -349,6 +350,34 @@ func TestPageAllocAllocToCache(t *testing.T) {
                        },
                },
        }
+       if PageAlloc64Bit != 0 {
+               const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
+
+               // This test is similar to the one with the same name for
+               // pageAlloc.alloc and serves the same purpose.
+               // See mpagealloc_test.go for details.
+               sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes)
+               baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1)
+               tests["DiscontiguousMappedSumBoundary"] = test{
+                       before: map[ChunkIdx][]BitRange{
+                               baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages - 1}},
+                               baseChunkIdx + chunkIdxBigJump:     {{1, PallocChunkPages - 1}},
+                       },
+                       scav: map[ChunkIdx][]BitRange{
+                               baseChunkIdx + sumsPerPhysPage - 1: {},
+                               baseChunkIdx + chunkIdxBigJump:     {},
+                       },
+                       hits: []PageCache{
+                               NewPageCache(PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-64), 1<<63, 0),
+                               NewPageCache(PageBase(baseChunkIdx+chunkIdxBigJump, 0), 1, 0),
+                               NewPageCache(0, 0, 0),
+                       },
+                       after: map[ChunkIdx][]BitRange{
+                               baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}},
+                               baseChunkIdx + chunkIdxBigJump:     {{0, PallocChunkPages}},
+                       },
+               }
+       }
        for name, v := range tests {
                v := v
                t.Run(name, func(t *testing.T) {
index f75cacf3f43eba630231dc3451a993bae2cea424..e0981377512f53dc28a72262d0b5800656309658 100644 (file)
@@ -1704,10 +1704,16 @@ func startTemplateThread() {
        if GOARCH == "wasm" { // no threads on wasm yet
                return
        }
+
+       // Disable preemption to guarantee that the template thread will be
+       // created before a park once haveTemplateThread is set.
+       mp := acquirem()
        if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) {
+               releasem(mp)
                return
        }
        newm(templateThread, nil)
+       releasem(mp)
 }
 
 // templateThread is a thread in a known-good state that exists solely
index a693937aa1e39c478e8eb1bda3f91b290efa7176..5f96d648d986b24dea48e20d0f51cb999a1d4336 100644 (file)
@@ -6,6 +6,7 @@ package runtime_test
 
 import (
        "fmt"
+       "internal/testenv"
        "math"
        "net"
        "runtime"
@@ -930,6 +931,29 @@ func TestLockOSThreadAvoidsStatePropagation(t *testing.T) {
        }
 }
 
+func TestLockOSThreadTemplateThreadRace(t *testing.T) {
+       testenv.MustHaveGoRun(t)
+
+       exe, err := buildTestProg(t, "testprog")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       iterations := 100
+       if testing.Short() {
+               // Reduce run time to ~100ms, with much lower probability of
+               // catching issues.
+               iterations = 5
+       }
+       for i := 0; i < iterations; i++ {
+               want := "OK\n"
+               output := runBuiltTestProg(t, exe, "LockOSThreadTemplateThreadRace")
+               if output != want {
+                       t.Fatalf("run %d: want %q, got %q", i, want, output)
+               }
+       }
+}
+
 // fakeSyscall emulates a system call.
 //go:nosplit
 func fakeSyscall(duration time.Duration) {
index fd3123e64743fbd8a13e1bb65e872634769ea493..098cc4dd722e659d5d97d0ea6a326d2efc893e8d 100644 (file)
@@ -7,6 +7,7 @@ package main
 import (
        "os"
        "runtime"
+       "sync"
        "time"
 )
 
@@ -30,6 +31,7 @@ func init() {
                runtime.LockOSThread()
        })
        register("LockOSThreadAvoidsStatePropagation", LockOSThreadAvoidsStatePropagation)
+       register("LockOSThreadTemplateThreadRace", LockOSThreadTemplateThreadRace)
 }
 
 func LockOSThreadMain() {
@@ -195,3 +197,50 @@ func LockOSThreadAvoidsStatePropagation() {
        runtime.UnlockOSThread()
        println("OK")
 }
+
+func LockOSThreadTemplateThreadRace() {
+       // This test attempts to reproduce the race described in
+       // golang.org/issue/38931. To do so, we must have a stop-the-world
+       // (achieved via ReadMemStats) racing with two LockOSThread calls.
+       //
+       // While this test attempts to line up the timing, it is only expected
+       // to fail (and thus hang) around 2% of the time if the race is
+       // present.
+
+       // Ensure enough Ps to actually run everything in parallel. Though on
+       // <4 core machines, we are still at the whim of the kernel scheduler.
+       runtime.GOMAXPROCS(4)
+
+       go func() {
+               // Stop the world; race with LockOSThread below.
+               var m runtime.MemStats
+               for {
+                       runtime.ReadMemStats(&m)
+               }
+       }()
+
+       // Try to synchronize both LockOSThreads.
+       start := time.Now().Add(10*time.Millisecond)
+
+       var wg sync.WaitGroup
+       wg.Add(2)
+
+       for i := 0; i < 2; i++ {
+               go func() {
+                       for time.Now().Before(start) {
+                       }
+
+                       // Add work to the local runq to trigger early startm
+                       // in handoffp.
+                       go func(){}()
+
+                       runtime.LockOSThread()
+                       runtime.Gosched()  // add a preemption point.
+                       wg.Done()
+               }()
+       }
+
+       wg.Wait()
+       // If both LockOSThreads completed then we did not hit the race.
+       println("OK")
+}
index 42979b5f4d5092e3771c828faa2fdecbd0fb5f6d..eb9d7fa47d3ecdf70613100fd52850be84da168d 100644 (file)
@@ -124,6 +124,11 @@ typedef struct {
 } Issue31891B;
 
 void callIssue31891(void);
+
+typedef struct {
+       int i;
+} Issue38408, *PIssue38408;
+
 */
 import "C"
 
@@ -552,3 +557,8 @@ func useIssue31891B(c *C.Issue31891B) {}
 func test31891(t *testing.T) {
        C.callIssue31891()
 }
+
+// issue 38408
+// A typedef pointer can be used as the element type.
+// No runtime test; just make sure it compiles.
+var _ C.PIssue38408 = &C.Issue38408{i: 1}