mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-03 00:34:21 +08:00
libgo: update to Go 1.14.4 release
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/241999
This commit is contained in:
parent
5311690085
commit
a5c9fb7995
@ -1,4 +1,4 @@
|
||||
761d68dacefc578e45ff299761f20989aef67823
|
||||
2ad0970e9da95024110cd3244e9e21313af70a5f
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -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.
|
||||
|
@ -1 +1 @@
|
||||
go1.14.2
|
||||
go1.14.4
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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])
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -79,37 +79,66 @@ type StringTag struct {
|
||||
NumberStr Number `json:",string"`
|
||||
}
|
||||
|
||||
var stringTagExpected = `{
|
||||
"BoolStr": "true",
|
||||
"IntStr": "42",
|
||||
"UintptrStr": "44",
|
||||
"StrStr": "\"xzbit\"",
|
||||
"NumberStr": "46"
|
||||
}`
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user