Commit a5c9fb79 by Ian Lance Taylor

libgo: update to Go 1.14.4 release

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/241999
parent 53116900
761d68dacefc578e45ff299761f20989aef67823 2ad0970e9da95024110cd3244e9e21313af70a5f
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
96745b980cfde139e8611772e2bc0c59a8e6cdf7 83b181c68bf332ac7948f145f33d128377a09c42
The first line of this file holds the git revision number of the The first line of this file holds the git revision number of the
last merge done from the master library sources. last merge done from the master library sources.
...@@ -2082,6 +2082,10 @@ var goIdent = make(map[string]*ast.Ident) ...@@ -2082,6 +2082,10 @@ var goIdent = make(map[string]*ast.Ident)
// that may contain a pointer. This is used for cgo pointer checking. // that may contain a pointer. This is used for cgo pointer checking.
var unionWithPointer = make(map[ast.Expr]bool) 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) { func (c *typeConv) Init(ptrSize, intSize int64) {
c.ptrSize = ptrSize c.ptrSize = ptrSize
c.intSize = intSize c.intSize = intSize
...@@ -2430,8 +2434,12 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ ...@@ -2430,8 +2434,12 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
break break
} }
if tag == "" { if tag == "" {
tag = "__" + strconv.Itoa(tagGen) tag = anonymousStructTag[dt]
tagGen++ if tag == "" {
tag = "__" + strconv.Itoa(tagGen)
tagGen++
anonymousStructTag[dt] = tag
}
} else if t.C.Empty() { } else if t.C.Empty() {
t.C.Set(dt.Kind + " " + tag) t.C.Set(dt.Kind + " " + tag)
} }
......
...@@ -1217,6 +1217,11 @@ func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) { ...@@ -1217,6 +1217,11 @@ func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) {
if r == -1 { if r == -1 {
return s, true 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) b := make([]byte, len(s)+2*utf8.UTFMax)
w := copy(b, s[0:r]) w := copy(b, s[0:r])
......
...@@ -2419,7 +2419,7 @@ func (m *textUnmarshalerString) UnmarshalText(text []byte) error { ...@@ -2419,7 +2419,7 @@ func (m *textUnmarshalerString) UnmarshalText(text []byte) error {
return nil 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. // See golang.org/issues/34437.
func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) { func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
var p map[textUnmarshalerString]string var p map[textUnmarshalerString]string
...@@ -2428,6 +2428,35 @@ func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) { ...@@ -2428,6 +2428,35 @@ func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
} }
if _, ok := p["foo"]; !ok { 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)
} }
} }
...@@ -635,11 +635,12 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) { ...@@ -635,11 +635,12 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
return return
} }
if opts.quoted { if opts.quoted {
b := make([]byte, 0, v.Len()+2) e2 := newEncodeState()
b = append(b, '"') // Since we encode the string twice, we only need to escape HTML
b = append(b, []byte(v.String())...) // the first time.
b = append(b, '"') e2.string(v.String(), opts.escapeHTML)
e.stringBytes(b, opts.escapeHTML) e.stringBytes(e2.Bytes(), false)
encodeStatePool.Put(e2)
} else { } else {
e.string(v.String(), opts.escapeHTML) e.string(v.String(), opts.escapeHTML)
} }
......
...@@ -79,37 +79,66 @@ type StringTag struct { ...@@ -79,37 +79,66 @@ type StringTag struct {
NumberStr Number `json:",string"` NumberStr Number `json:",string"`
} }
var stringTagExpected = `{ func TestRoundtripStringTag(t *testing.T) {
"BoolStr": "true", tests := []struct {
"IntStr": "42", name string
"UintptrStr": "44", in StringTag
"StrStr": "\"xzbit\"", want string // empty to just test that we roundtrip
"NumberStr": "46" }{
}` {
name: "AllTypes",
func TestStringTag(t *testing.T) { in: StringTag{
var s StringTag BoolStr: true,
s.BoolStr = true IntStr: 42,
s.IntStr = 42 UintptrStr: 44,
s.UintptrStr = 44 StrStr: "xzbit",
s.StrStr = "xzbit" NumberStr: "46",
s.NumberStr = "46" },
got, err := MarshalIndent(&s, "", " ") want: `{
if err != nil { "BoolStr": "true",
t.Fatal(err) "IntStr": "42",
} "UintptrStr": "44",
if got := string(got); got != stringTagExpected { "StrStr": "\"xzbit\"",
t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected) "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. // Verify that it round-trips.
var s2 StringTag var s2 StringTag
err = NewDecoder(bytes.NewReader(got)).Decode(&s2) if err := Unmarshal(got, &s2); err != nil {
if err != nil { t.Fatalf("Decode: %v", err)
t.Fatalf("Decode: %v", err) }
} if !reflect.DeepEqual(test.in, s2) {
if !reflect.DeepEqual(s, s2) { t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2) }
})
} }
} }
......
...@@ -144,14 +144,15 @@ func TestEncoderSetEscapeHTML(t *testing.T) { ...@@ -144,14 +144,15 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
}, },
{ {
"stringOption", stringOption, "stringOption", stringOption,
`{"bar":"\"\u003chtml\u003efoobar\u003c/html\u003e\""}`, `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
`{"bar":"\"<html>foobar</html>\""}`, `{"bar":"\"<html>foobar</html>\""}`,
}, },
} { } {
var buf bytes.Buffer var buf bytes.Buffer
enc := NewEncoder(&buf) enc := NewEncoder(&buf)
if err := enc.Encode(tt.v); err != nil { 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 { if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape) t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
...@@ -159,7 +160,8 @@ func TestEncoderSetEscapeHTML(t *testing.T) { ...@@ -159,7 +160,8 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
buf.Reset() buf.Reset()
enc.SetEscapeHTML(false) enc.SetEscapeHTML(false)
if err := enc.Encode(tt.v); err != nil { 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 { if got := strings.TrimSpace(buf.String()); got != tt.want {
t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q", t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
......
...@@ -62,9 +62,6 @@ func Examples(testFiles ...*ast.File) []*Example { ...@@ -62,9 +62,6 @@ func Examples(testFiles ...*ast.File) []*Example {
if !ok || f.Recv != nil { if !ok || f.Recv != nil {
continue continue
} }
if params := f.Type.Params; len(params.List) != 0 {
continue // function has params; not a valid example
}
numDecl++ numDecl++
name := f.Name.Name name := f.Name.Name
if isTest(name, "Test") || isTest(name, "Benchmark") { if isTest(name, "Test") || isTest(name, "Benchmark") {
...@@ -74,6 +71,9 @@ func Examples(testFiles ...*ast.File) []*Example { ...@@ -74,6 +71,9 @@ func Examples(testFiles ...*ast.File) []*Example {
if !isTest(name, "Example") { if !isTest(name, "Example") {
continue 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) if f.Body == nil { // ast.File.Body nil dereference (see issue 28044)
continue continue
} }
......
...@@ -331,25 +331,65 @@ func main() { ...@@ -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) { func TestExamplesWholeFile(t *testing.T) {
fset := token.NewFileSet() for _, c := range exampleWholeFileTestCases {
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleWholeFile), parser.ParseComments) fset := token.NewFileSet()
if err != nil { file, err := parser.ParseFile(fset, "test.go", strings.NewReader(c.Source), parser.ParseComments)
t.Fatal(err) if err != nil {
} t.Fatal(err)
es := doc.Examples(file) }
if len(es) != 1 { es := doc.Examples(file)
t.Fatalf("wrong number of examples; got %d want 1", len(es)) 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 != "" { e := es[0]
t.Errorf("got Name == %q, want %q", e.Name, "") if e.Name != "" {
} t.Errorf("%s: got Name == %q, want %q", c.Title, 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 := 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, "Hello, world!\n"; g != w { }
t.Errorf("got Output == %q, want %q", g, w) if g, w := e.Output, c.Output; g != w {
t.Errorf("%s: got Output == %q, want %q", c.Title, g, w)
}
} }
} }
......
...@@ -133,13 +133,7 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) ...@@ -133,13 +133,7 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
// first error encountered are returned. // 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) { 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) list, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
defer fd.Close()
list, err := fd.Readdir(-1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -740,7 +740,8 @@ func (z nat) divLarge(u, uIn, vIn nat) (q, r nat) { ...@@ -740,7 +740,8 @@ func (z nat) divLarge(u, uIn, vIn nat) (q, r nat) {
// The remainder overwrites input u. // The remainder overwrites input u.
// //
// Precondition: // 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) { func (q nat) divBasic(u, v nat) {
n := len(v) n := len(v)
m := len(u) - n m := len(u) - n
...@@ -779,6 +780,8 @@ func (q nat) divBasic(u, v nat) { ...@@ -779,6 +780,8 @@ func (q nat) divBasic(u, v nat) {
} }
// D4. // 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) qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0)
qhl := len(qhatv) qhl := len(qhatv)
if j+qhl > len(u) && qhatv[n] == 0 { if j+qhl > len(u) && qhatv[n] == 0 {
...@@ -787,7 +790,11 @@ func (q nat) divBasic(u, v nat) { ...@@ -787,7 +790,11 @@ func (q nat) divBasic(u, v nat) {
c := subVV(u[j:j+qhl], u[j:], qhatv) c := subVV(u[j:j+qhl], u[j:], qhatv)
if c != 0 { if c != 0 {
c := addVV(u[j:j+n], u[j:], v) 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-- qhat--
} }
...@@ -827,6 +834,10 @@ func (z nat) divRecursive(u, v nat) { ...@@ -827,6 +834,10 @@ func (z nat) divRecursive(u, v nat) {
putNat(tmp) 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) { func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) {
u = u.norm() u = u.norm()
v = v.norm() v = v.norm()
......
...@@ -786,3 +786,21 @@ func TestNatDiv(t *testing.T) { ...@@ -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)
}
}
...@@ -2450,3 +2450,38 @@ func TestDirSeek(t *testing.T) { ...@@ -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())
}
}
...@@ -55,6 +55,16 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string { ...@@ -55,6 +55,16 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
t.Fatal(err) 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 := testenv.CleanCmdEnv(exec.Command(exe, name))
cmd.Env = append(cmd.Env, env...) cmd.Env = append(cmd.Env, env...)
if testing.Short() { if testing.Short() {
...@@ -64,7 +74,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string { ...@@ -64,7 +74,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
cmd.Stdout = &b cmd.Stdout = &b
cmd.Stderr = &b cmd.Stderr = &b
if err := cmd.Start(); err != nil { 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, // If the process doesn't complete within 1 minute,
...@@ -92,7 +102,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string { ...@@ -92,7 +102,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
}() }()
if err := cmd.Wait(); err != nil { 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) close(done)
......
...@@ -288,6 +288,28 @@ func bgscavenge(c chan int) { ...@@ -288,6 +288,28 @@ func bgscavenge(c chan int) {
continue 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 // Multiply the critical time by 1 + the ratio of the costs of using
// scavenged memory vs. scavenging memory. This forces us to pay down // scavenged memory vs. scavenging memory. This forces us to pay down
// the cost of reusing this memory eagerly by sleeping for a longer period // the cost of reusing this memory eagerly by sleeping for a longer period
......
...@@ -148,9 +148,14 @@ func (s *pageAlloc) allocToCache() pageCache { ...@@ -148,9 +148,14 @@ func (s *pageAlloc) allocToCache() pageCache {
// Update as an allocation, but note that it's not contiguous. // Update as an allocation, but note that it's not contiguous.
s.update(c.base, pageCachePages, false, true) s.update(c.base, pageCachePages, false, true)
// We're always searching for the first free page, and we always know the // Set the search address to the last page represented by the cache.
// up to pageCache size bits will be allocated, so we can always move the // Since all of the pages in this block are going to the cache, and we
// searchAddr past the cache. // searched for the first free page, we can confidently start at the
s.searchAddr = c.base + pageSize*pageCachePages // 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 return c
} }
...@@ -260,12 +260,13 @@ func TestPageAllocAllocToCache(t *testing.T) { ...@@ -260,12 +260,13 @@ func TestPageAllocAllocToCache(t *testing.T) {
if GOOS == "openbsd" && testing.Short() { if GOOS == "openbsd" && testing.Short() {
t.Skip("skipping because virtual memory is limited; see #36210") t.Skip("skipping because virtual memory is limited; see #36210")
} }
tests := map[string]struct { type test struct {
before map[ChunkIdx][]BitRange before map[ChunkIdx][]BitRange
scav map[ChunkIdx][]BitRange scav map[ChunkIdx][]BitRange
hits []PageCache // expected base addresses and patterns hits []PageCache // expected base addresses and patterns
after map[ChunkIdx][]BitRange after map[ChunkIdx][]BitRange
}{ }
tests := map[string]test{
"AllFree": { "AllFree": {
before: map[ChunkIdx][]BitRange{ before: map[ChunkIdx][]BitRange{
BaseChunkIdx: {}, BaseChunkIdx: {},
...@@ -349,6 +350,34 @@ func TestPageAllocAllocToCache(t *testing.T) { ...@@ -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 { for name, v := range tests {
v := v v := v
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
......
...@@ -1704,10 +1704,16 @@ func startTemplateThread() { ...@@ -1704,10 +1704,16 @@ func startTemplateThread() {
if GOARCH == "wasm" { // no threads on wasm yet if GOARCH == "wasm" { // no threads on wasm yet
return 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) { if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) {
releasem(mp)
return return
} }
newm(templateThread, nil) newm(templateThread, nil)
releasem(mp)
} }
// templateThread is a thread in a known-good state that exists solely // templateThread is a thread in a known-good state that exists solely
......
...@@ -6,6 +6,7 @@ package runtime_test ...@@ -6,6 +6,7 @@ package runtime_test
import ( import (
"fmt" "fmt"
"internal/testenv"
"math" "math"
"net" "net"
"runtime" "runtime"
...@@ -930,6 +931,29 @@ func TestLockOSThreadAvoidsStatePropagation(t *testing.T) { ...@@ -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. // fakeSyscall emulates a system call.
//go:nosplit //go:nosplit
func fakeSyscall(duration time.Duration) { func fakeSyscall(duration time.Duration) {
......
...@@ -7,6 +7,7 @@ package main ...@@ -7,6 +7,7 @@ package main
import ( import (
"os" "os"
"runtime" "runtime"
"sync"
"time" "time"
) )
...@@ -30,6 +31,7 @@ func init() { ...@@ -30,6 +31,7 @@ func init() {
runtime.LockOSThread() runtime.LockOSThread()
}) })
register("LockOSThreadAvoidsStatePropagation", LockOSThreadAvoidsStatePropagation) register("LockOSThreadAvoidsStatePropagation", LockOSThreadAvoidsStatePropagation)
register("LockOSThreadTemplateThreadRace", LockOSThreadTemplateThreadRace)
} }
func LockOSThreadMain() { func LockOSThreadMain() {
...@@ -195,3 +197,50 @@ func LockOSThreadAvoidsStatePropagation() { ...@@ -195,3 +197,50 @@ func LockOSThreadAvoidsStatePropagation() {
runtime.UnlockOSThread() runtime.UnlockOSThread()
println("OK") 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")
}
...@@ -124,6 +124,11 @@ typedef struct { ...@@ -124,6 +124,11 @@ typedef struct {
} Issue31891B; } Issue31891B;
void callIssue31891(void); void callIssue31891(void);
typedef struct {
int i;
} Issue38408, *PIssue38408;
*/ */
import "C" import "C"
...@@ -552,3 +557,8 @@ func useIssue31891B(c *C.Issue31891B) {} ...@@ -552,3 +557,8 @@ func useIssue31891B(c *C.Issue31891B) {}
func test31891(t *testing.T) { func test31891(t *testing.T) {
C.callIssue31891() 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}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment