mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.simd] all: merge master (740857f) into dev.simd
Merge List: + 2025-06-30740857f529runtime: stash allpSnapshot on the M + 2025-06-309ae38be302sync: disassociate WaitGroups from bubbles on Wait + 2025-06-304731832342crypto/hmac: wrap ErrUnsupported returned by Clone + 2025-06-3003ad694dcbruntime: update skips for TestGdbBacktrace + 2025-06-309d1cd0b881iter: add missing type parameter in doc + 2025-06-29acb914f2c2cmd/doc: fix -http on Windows + 2025-06-27b51f1cdb87runtime: remove arbitrary 5-second timeout in TestNeedmDeadlock + 2025-06-27f1e6ae2f6freflect: fix TypeAssert on nil interface values + 2025-06-27e81c624656os: use minimal file permissions when opening parent directory in RemoveAll + 2025-06-272a22aefa1fencoding/json: add security section to doc + 2025-06-27742fda9524runtime: account for missing frame pointer in preamble + 2025-06-27fdc076ce76net/http: fix RoundTrip context cancellation for js/wasm + 2025-06-27d9d2cadd63encoding/json: fix typo in hotlink for jsontext.PreserveRawStrings + 2025-06-260f8ab2db17cmd/link: permit a larger size BSS reference to a smaller DATA symbol + 2025-06-26988a20c8c5cmd/compile/internal/escape: evaluate any side effects when rewriting with literals + 2025-06-25b5d555991aencoding/json/jsontext: remove Encoder.UnusedBuffer + 2025-06-250b4d2eab2fencoding/json/jsontext: rename Encoder.UnusedBuffer as Encoder.AvailableBuffer Change-Id: Iea44ab825bdf087fbe7570df8d2d66d1d3327c31
This commit is contained in:
commit
f849225b3b
39 changed files with 537 additions and 139 deletions
|
|
@ -42,6 +42,8 @@ func TestASAN(t *testing.T) {
|
||||||
{src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"},
|
{src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"},
|
||||||
{src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"},
|
{src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"},
|
||||||
{src: "asan_global5.go"},
|
{src: "asan_global5.go"},
|
||||||
|
{src: "asan_global_asm"},
|
||||||
|
{src: "asan_global_asm2_fail", memoryAccessError: "global-buffer-overflow", errorLocation: "main.go:17"},
|
||||||
{src: "arena_fail.go", memoryAccessError: "use-after-poison", errorLocation: "arena_fail.go:26", experiments: []string{"arenas"}},
|
{src: "arena_fail.go", memoryAccessError: "use-after-poison", errorLocation: "arena_fail.go:26", experiments: []string{"arenas"}},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,7 @@ func (c *config) checkRuntime() (skip bool, err error) {
|
||||||
|
|
||||||
// srcPath returns the path to the given file relative to this test's source tree.
|
// srcPath returns the path to the given file relative to this test's source tree.
|
||||||
func srcPath(path string) string {
|
func srcPath(path string) string {
|
||||||
return filepath.Join("testdata", path)
|
return "./testdata/" + path
|
||||||
}
|
}
|
||||||
|
|
||||||
// A tempDir manages a temporary directory within a test.
|
// A tempDir manages a temporary directory within a test.
|
||||||
|
|
|
||||||
8
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/asm.s
vendored
Normal file
8
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/asm.s
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·x(SB)/8, $123
|
||||||
|
GLOBL ·x(SB), NOPTR, $8
|
||||||
11
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/main.go
vendored
Normal file
11
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/main.go
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
var x uint64
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println(x)
|
||||||
|
}
|
||||||
8
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/asm.s
vendored
Normal file
8
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/asm.s
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·x(SB)/8, $123
|
||||||
|
GLOBL ·x(SB), NOPTR, $8
|
||||||
20
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/main.go
vendored
Normal file
20
src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/main.go
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var x uint64
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
bar(&x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bar(a *uint64) {
|
||||||
|
p := (*uint64)(unsafe.Add(unsafe.Pointer(a), 1*unsafe.Sizeof(uint64(1))))
|
||||||
|
if *p == 10 { // BOOM
|
||||||
|
println("its value is 10")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -545,6 +545,14 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
||||||
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
|
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assignTemp := func(n ir.Node, init *ir.Nodes) {
|
||||||
|
// Preserve any side effects of n by assigning it to an otherwise unused temp.
|
||||||
|
pos := n.Pos()
|
||||||
|
tmp := typecheck.TempAt(pos, fn, n.Type())
|
||||||
|
init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
|
||||||
|
init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, n)))
|
||||||
|
}
|
||||||
|
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
case ir.OMAKESLICE:
|
case ir.OMAKESLICE:
|
||||||
// Check if we can replace a non-constant argument to make with
|
// Check if we can replace a non-constant argument to make with
|
||||||
|
|
@ -556,15 +564,19 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
||||||
r = &n.Len
|
r = &n.Len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*r).Op() != ir.OLITERAL {
|
||||||
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
|
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
|
||||||
lit, ok := s.(*ir.BasicLit)
|
lit, ok := s.(*ir.BasicLit)
|
||||||
if !ok || lit.Val().Kind() != constant.Int {
|
if !ok || lit.Val().Kind() != constant.Int {
|
||||||
base.Fatalf("unexpected BasicLit Kind")
|
base.Fatalf("unexpected BasicLit Kind")
|
||||||
}
|
}
|
||||||
if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
|
if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
|
||||||
|
// Preserve any side effects of the original expression, then replace it.
|
||||||
|
assignTemp(*r, n.PtrInit())
|
||||||
*r = lit
|
*r = lit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case ir.OCONVIFACE:
|
case ir.OCONVIFACE:
|
||||||
// Check if we can replace a non-constant expression in an interface conversion with
|
// Check if we can replace a non-constant expression in an interface conversion with
|
||||||
// a literal to avoid heap allocating the underlying interface value.
|
// a literal to avoid heap allocating the underlying interface value.
|
||||||
|
|
@ -575,6 +587,8 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
||||||
if base.Debug.EscapeDebug >= 3 {
|
if base.Debug.EscapeDebug >= 3 {
|
||||||
base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
|
base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
|
||||||
}
|
}
|
||||||
|
// Preserve any side effects of the original expression, then replace it.
|
||||||
|
assignTemp(conv.X, conv.PtrInit())
|
||||||
v := v.(*ir.BasicLit)
|
v := v.(*ir.BasicLit)
|
||||||
conv.X = ir.NewBasicLit(conv.X.Pos(), conv.X.Type(), v.Val())
|
conv.X = ir.NewBasicLit(conv.X.Pos(), conv.X.Type(), v.Val())
|
||||||
typecheck.Expr(conv)
|
typecheck.Expr(conv)
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,8 @@
|
||||||
//
|
//
|
||||||
// go doc
|
// go doc
|
||||||
// Show documentation for current package.
|
// Show documentation for current package.
|
||||||
|
// go doc -http
|
||||||
|
// Serve HTML documentation over HTTP for the current package.
|
||||||
// go doc Foo
|
// go doc Foo
|
||||||
// Show documentation for Foo in the current package.
|
// Show documentation for Foo in the current package.
|
||||||
// (Foo starts with a capital letter so it cannot match
|
// (Foo starts with a capital letter so it cannot match
|
||||||
|
|
@ -447,6 +449,8 @@
|
||||||
// Treat a command (package main) like a regular package.
|
// Treat a command (package main) like a regular package.
|
||||||
// Otherwise package main's exported symbols are hidden
|
// Otherwise package main's exported symbols are hidden
|
||||||
// when showing the package's top-level documentation.
|
// when showing the package's top-level documentation.
|
||||||
|
// -http
|
||||||
|
// Serve HTML docs over HTTP.
|
||||||
// -short
|
// -short
|
||||||
// One-line representation for each symbol.
|
// One-line representation for each symbol.
|
||||||
// -src
|
// -src
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@ different cases. If this occurs, documentation for all matches is printed.
|
||||||
Examples:
|
Examples:
|
||||||
go doc
|
go doc
|
||||||
Show documentation for current package.
|
Show documentation for current package.
|
||||||
|
go doc -http
|
||||||
|
Serve HTML documentation over HTTP for the current package.
|
||||||
go doc Foo
|
go doc Foo
|
||||||
Show documentation for Foo in the current package.
|
Show documentation for Foo in the current package.
|
||||||
(Foo starts with a capital letter so it cannot match
|
(Foo starts with a capital letter so it cannot match
|
||||||
|
|
@ -116,6 +118,8 @@ Flags:
|
||||||
Treat a command (package main) like a regular package.
|
Treat a command (package main) like a regular package.
|
||||||
Otherwise package main's exported symbols are hidden
|
Otherwise package main's exported symbols are hidden
|
||||||
when showing the package's top-level documentation.
|
when showing the package's top-level documentation.
|
||||||
|
-http
|
||||||
|
Serve HTML docs over HTTP.
|
||||||
-short
|
-short
|
||||||
One-line representation for each symbol.
|
One-line representation for each symbol.
|
||||||
-src
|
-src
|
||||||
|
|
|
||||||
|
|
@ -227,8 +227,16 @@ func doPkgsite(urlPath string) error {
|
||||||
fields := strings.Fields(vars)
|
fields := strings.Fields(vars)
|
||||||
if err == nil && len(fields) == 2 {
|
if err == nil && len(fields) == 2 {
|
||||||
goproxy, gomodcache := fields[0], fields[1]
|
goproxy, gomodcache := fields[0], fields[1]
|
||||||
goproxy = "file://" + filepath.Join(gomodcache, "cache", "download") + "," + goproxy
|
gomodcache = filepath.Join(gomodcache, "cache", "download")
|
||||||
env = append(env, "GOPROXY="+goproxy)
|
// Convert absolute path to file URL. pkgsite will not accept
|
||||||
|
// Windows absolute paths because they look like a host:path remote.
|
||||||
|
// TODO(golang.org/issue/32456): use url.FromFilePath when implemented.
|
||||||
|
if strings.HasPrefix(gomodcache, "/") {
|
||||||
|
gomodcache = "file://" + gomodcache
|
||||||
|
} else {
|
||||||
|
gomodcache = "file:///" + filepath.ToSlash(gomodcache)
|
||||||
|
}
|
||||||
|
env = append(env, "GOPROXY="+gomodcache+","+goproxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = "v0.0.0-20250608123103-82c52f1754cd"
|
const version = "v0.0.0-20250608123103-82c52f1754cd"
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,12 @@ type Loader struct {
|
||||||
|
|
||||||
WasmExports []Sym
|
WasmExports []Sym
|
||||||
|
|
||||||
|
// sizeFixups records symbols that we need to fix up the size
|
||||||
|
// after loading. It is very rarely needed, only for a DATA symbol
|
||||||
|
// and a BSS symbol with the same name, and the BSS symbol has
|
||||||
|
// larger size.
|
||||||
|
sizeFixups []symAndSize
|
||||||
|
|
||||||
flags uint32
|
flags uint32
|
||||||
|
|
||||||
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
|
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
|
||||||
|
|
@ -469,18 +475,17 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in
|
||||||
// In summary, the "overwrite" variable and the final result are
|
// In summary, the "overwrite" variable and the final result are
|
||||||
//
|
//
|
||||||
// new sym old sym result
|
// new sym old sym result
|
||||||
// ---------------------------------------------
|
// -------------------------------------------------------
|
||||||
// TEXT BSS new wins
|
// TEXT BSS new wins
|
||||||
// DATA DATA ERROR
|
// DATA DATA ERROR
|
||||||
// DATA lg/eq BSS sm/eq new wins
|
// DATA lg/eq BSS sm/eq new wins
|
||||||
// DATA small BSS large ERROR
|
// DATA small BSS large merge: new with larger size
|
||||||
// BSS large DATA small ERROR
|
// BSS large DATA small merge: old with larger size
|
||||||
// BSS large BSS small new wins
|
// BSS large BSS small new wins
|
||||||
// BSS sm/eq D/B lg/eq old wins
|
// BSS sm/eq D/B lg/eq old wins
|
||||||
// BSS TEXT old wins
|
// BSS TEXT old wins
|
||||||
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
|
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
|
||||||
newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
|
newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
|
||||||
oldIsText := oldtyp.IsText()
|
|
||||||
newIsText := newtyp.IsText()
|
newIsText := newtyp.IsText()
|
||||||
oldHasContent := oldr.DataSize(oldli) != 0
|
oldHasContent := oldr.DataSize(oldli) != 0
|
||||||
newHasContent := r.DataSize(li) != 0
|
newHasContent := r.DataSize(li) != 0
|
||||||
|
|
@ -488,12 +493,28 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in
|
||||||
newIsBSS := newtyp.IsData() && !newHasContent
|
newIsBSS := newtyp.IsData() && !newHasContent
|
||||||
switch {
|
switch {
|
||||||
case newIsText && oldIsBSS,
|
case newIsText && oldIsBSS,
|
||||||
newHasContent && oldIsBSS && sz >= oldsz,
|
newHasContent && oldIsBSS,
|
||||||
newIsBSS && oldIsBSS && sz > oldsz:
|
newIsBSS && oldIsBSS && sz > oldsz:
|
||||||
// new symbol overwrites old symbol.
|
// new symbol overwrites old symbol.
|
||||||
l.objSyms[oldi] = objSym{r.objidx, li}
|
l.objSyms[oldi] = objSym{r.objidx, li}
|
||||||
case newIsBSS && (oldsz >= sz || oldIsText):
|
if oldsz > sz {
|
||||||
|
// If the BSS symbol has a larger size, expand the data
|
||||||
|
// symbol's size so access from the BSS side cannot overrun.
|
||||||
|
// It is hard to modify the symbol size until all Go objects
|
||||||
|
// (potentially read-only) are loaded, so we record it in
|
||||||
|
// a fixup table and apply them later. This is very rare.
|
||||||
|
// One case is a global variable with a Go declaration and an
|
||||||
|
// assembly definition, which typically have the same size,
|
||||||
|
// but in ASAN mode the Go declaration has a larger size due
|
||||||
|
// to the inserted red zone.
|
||||||
|
l.sizeFixups = append(l.sizeFixups, symAndSize{oldi, uint32(oldsz)})
|
||||||
|
}
|
||||||
|
case newIsBSS:
|
||||||
// old win, just ignore the new symbol.
|
// old win, just ignore the new symbol.
|
||||||
|
if sz > oldsz {
|
||||||
|
// See the comment above for sizeFixups.
|
||||||
|
l.sizeFixups = append(l.sizeFixups, symAndSize{oldi, uint32(sz)})
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz)
|
log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz)
|
||||||
}
|
}
|
||||||
|
|
@ -2277,6 +2298,10 @@ func (l *Loader) LoadSyms(arch *sys.Arch) {
|
||||||
st.preloadSyms(r, hashedDef)
|
st.preloadSyms(r, hashedDef)
|
||||||
st.preloadSyms(r, nonPkgDef)
|
st.preloadSyms(r, nonPkgDef)
|
||||||
}
|
}
|
||||||
|
for _, sf := range l.sizeFixups {
|
||||||
|
pp := l.cloneToExternal(sf.sym)
|
||||||
|
pp.size = int64(sf.size)
|
||||||
|
}
|
||||||
for _, vr := range st.linknameVarRefs {
|
for _, vr := range st.linknameVarRefs {
|
||||||
l.checkLinkname(vr.pkg, vr.name, vr.sym)
|
l.checkLinkname(vr.pkg, vr.name, vr.sym)
|
||||||
}
|
}
|
||||||
|
|
@ -2498,7 +2523,7 @@ func topLevelSym(sname string, skind sym.SymKind) bool {
|
||||||
// a symbol originally discovered as part of an object file, it's
|
// a symbol originally discovered as part of an object file, it's
|
||||||
// easier to do this if we make the updates to an external symbol
|
// easier to do this if we make the updates to an external symbol
|
||||||
// payload.
|
// payload.
|
||||||
func (l *Loader) cloneToExternal(symIdx Sym) {
|
func (l *Loader) cloneToExternal(symIdx Sym) *extSymPayload {
|
||||||
if l.IsExternal(symIdx) {
|
if l.IsExternal(symIdx) {
|
||||||
panic("sym is already external, no need for clone")
|
panic("sym is already external, no need for clone")
|
||||||
}
|
}
|
||||||
|
|
@ -2550,6 +2575,8 @@ func (l *Loader) cloneToExternal(symIdx Sym) {
|
||||||
// Some attributes were encoded in the object file. Copy them over.
|
// Some attributes were encoded in the object file. Copy them over.
|
||||||
l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
|
l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
|
||||||
l.SetAttrShared(symIdx, r.Shared())
|
l.SetAttrShared(symIdx, r.Shared())
|
||||||
|
|
||||||
|
return pp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the payload of symbol src to dst. Both src and dst must be external
|
// Copy the payload of symbol src to dst. Both src and dst must be external
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -583,6 +584,18 @@ func TestHMAC(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoClone(t *testing.T) {
|
||||||
|
h := New(func() hash.Hash { return justHash{sha256.New()} }, []byte("key"))
|
||||||
|
if _, ok := h.(hash.Cloner); !ok {
|
||||||
|
t.Skip("no Cloner support")
|
||||||
|
}
|
||||||
|
h.Write([]byte("test"))
|
||||||
|
_, err := h.(hash.Cloner).Clone()
|
||||||
|
if !errors.Is(err, errors.ErrUnsupported) {
|
||||||
|
t.Errorf("Clone() = %v, want ErrUnsupported", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNonUniqueHash(t *testing.T) {
|
func TestNonUniqueHash(t *testing.T) {
|
||||||
if boring.Enabled {
|
if boring.Enabled {
|
||||||
t.Skip("hash.Hash provided by boringcrypto are not comparable")
|
t.Skip("hash.Hash provided by boringcrypto are not comparable")
|
||||||
|
|
|
||||||
|
|
@ -130,26 +130,36 @@ func (h *HMAC) Reset() {
|
||||||
h.marshaled = true
|
h.marshaled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errCloneUnsupported struct{}
|
||||||
|
|
||||||
|
func (e errCloneUnsupported) Error() string {
|
||||||
|
return "crypto/hmac: hash does not support hash.Cloner"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e errCloneUnsupported) Unwrap() error {
|
||||||
|
return errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
// Clone implements [hash.Cloner] if the underlying hash does.
|
// Clone implements [hash.Cloner] if the underlying hash does.
|
||||||
// Otherwise, it returns [errors.ErrUnsupported].
|
// Otherwise, it returns an error wrapping [errors.ErrUnsupported].
|
||||||
func (h *HMAC) Clone() (hash.Cloner, error) {
|
func (h *HMAC) Clone() (hash.Cloner, error) {
|
||||||
r := *h
|
r := *h
|
||||||
ic, ok := h.inner.(hash.Cloner)
|
ic, ok := h.inner.(hash.Cloner)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.ErrUnsupported
|
return nil, errCloneUnsupported{}
|
||||||
}
|
}
|
||||||
oc, ok := h.outer.(hash.Cloner)
|
oc, ok := h.outer.(hash.Cloner)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.ErrUnsupported
|
return nil, errCloneUnsupported{}
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
r.inner, err = ic.Clone()
|
r.inner, err = ic.Clone()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.ErrUnsupported
|
return nil, errCloneUnsupported{}
|
||||||
}
|
}
|
||||||
r.outer, err = oc.Clone()
|
r.outer, err = oc.Clone()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.ErrUnsupported
|
return nil, errCloneUnsupported{}
|
||||||
}
|
}
|
||||||
return &r, nil
|
return &r, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,14 @@ import (
|
||||||
// and the input is a JSON quoted string, Unmarshal calls
|
// and the input is a JSON quoted string, Unmarshal calls
|
||||||
// [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string.
|
// [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string.
|
||||||
//
|
//
|
||||||
// To unmarshal JSON into a struct, Unmarshal matches incoming object
|
// To unmarshal JSON into a struct, Unmarshal matches incoming object keys to
|
||||||
// keys to the keys used by [Marshal] (either the struct field name or its tag),
|
// the keys used by [Marshal] (either the struct field name or its tag),
|
||||||
// preferring an exact match but also accepting a case-insensitive match. By
|
// ignoring case. If multiple struct fields match an object key, an exact case
|
||||||
// default, object keys which don't have a corresponding struct field are
|
// match is preferred over a case-insensitive one.
|
||||||
// ignored (see [Decoder.DisallowUnknownFields] for an alternative).
|
//
|
||||||
|
// Incoming object members are processed in the order observed. If an object
|
||||||
|
// includes duplicate keys, later duplicates will replace or be merged into
|
||||||
|
// prior values.
|
||||||
//
|
//
|
||||||
// To unmarshal JSON into an interface value,
|
// To unmarshal JSON into an interface value,
|
||||||
// Unmarshal stores one of these in the interface value:
|
// Unmarshal stores one of these in the interface value:
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,44 @@
|
||||||
|
|
||||||
//go:build !goexperiment.jsonv2
|
//go:build !goexperiment.jsonv2
|
||||||
|
|
||||||
// Package json implements encoding and decoding of JSON as defined in
|
// Package json implements encoding and decoding of JSON as defined in RFC 7159.
|
||||||
// RFC 7159. The mapping between JSON and Go values is described
|
// The mapping between JSON and Go values is described in the documentation for
|
||||||
// in the documentation for the Marshal and Unmarshal functions.
|
// the Marshal and Unmarshal functions.
|
||||||
//
|
//
|
||||||
// See "JSON and Go" for an introduction to this package:
|
// See "JSON and Go" for an introduction to this package:
|
||||||
// https://golang.org/doc/articles/json_and_go.html
|
// https://golang.org/doc/articles/json_and_go.html
|
||||||
|
//
|
||||||
|
// # Security Considerations
|
||||||
|
//
|
||||||
|
// The JSON standard (RFC 7159) is lax in its definition of a number of parser
|
||||||
|
// behaviors. As such, many JSON parsers behave differently in various
|
||||||
|
// scenarios. These differences in parsers mean that systems that use multiple
|
||||||
|
// independent JSON parser implementations may parse the same JSON object in
|
||||||
|
// differing ways.
|
||||||
|
//
|
||||||
|
// Systems that rely on a JSON object being parsed consistently for security
|
||||||
|
// purposes should be careful to understand the behaviors of this parser, as
|
||||||
|
// well as how these behaviors may cause interoperability issues with other
|
||||||
|
// parser implementations.
|
||||||
|
//
|
||||||
|
// Due to the Go Backwards Compatibility promise (https://go.dev/doc/go1compat)
|
||||||
|
// there are a number of behaviors this package exhibits that may cause
|
||||||
|
// interopability issues, but cannot be changed. In particular the following
|
||||||
|
// parsing behaviors may cause issues:
|
||||||
|
//
|
||||||
|
// - If a JSON object contains duplicate keys, keys are processed in the order
|
||||||
|
// they are observed, meaning later values will replace or be merged into
|
||||||
|
// prior values, depending on the field type (in particular maps and structs
|
||||||
|
// will have values merged, while other types have values replaced).
|
||||||
|
// - When parsing a JSON object into a Go struct, keys are considered in a
|
||||||
|
// case-insensitive fashion.
|
||||||
|
// - When parsing a JSON object into a Go struct, unknown keys in the JSON
|
||||||
|
// object are ignored (unless a [Decoder] is used and
|
||||||
|
// [Decoder.DisallowUnknownFields] has been called).
|
||||||
|
// - Invalid UTF-8 bytes in JSON strings are replaced by the Unicode
|
||||||
|
// replacement character.
|
||||||
|
// - Large JSON number integers will lose precision when unmarshaled into
|
||||||
|
// floating-point types.
|
||||||
package json
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ type encodeBuffer struct {
|
||||||
|
|
||||||
// maxValue is the approximate maximum Value size passed to WriteValue.
|
// maxValue is the approximate maximum Value size passed to WriteValue.
|
||||||
maxValue int
|
maxValue int
|
||||||
// unusedCache is the buffer returned by the UnusedBuffer method.
|
// availBuffer is the buffer returned by the AvailableBuffer method.
|
||||||
unusedCache []byte
|
availBuffer []byte // always has zero length
|
||||||
// bufStats is statistics about buffer utilization.
|
// bufStats is statistics about buffer utilization.
|
||||||
// It is only used with pooled encoders in pools.go.
|
// It is only used with pooled encoders in pools.go.
|
||||||
bufStats bufferStatistics
|
bufStats bufferStatistics
|
||||||
|
|
@ -465,9 +465,9 @@ func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) (
|
||||||
isVerbatim := safeASCII || !jsonwire.NeedEscape(b[pos+len(`"`):len(b)-len(`"`)])
|
isVerbatim := safeASCII || !jsonwire.NeedEscape(b[pos+len(`"`):len(b)-len(`"`)])
|
||||||
if !isVerbatim {
|
if !isVerbatim {
|
||||||
var err error
|
var err error
|
||||||
b2 := append(e.unusedCache, b[pos+len(`"`):len(b)-len(`"`)]...)
|
b2 := append(e.availBuffer, b[pos+len(`"`):len(b)-len(`"`)]...)
|
||||||
b, err = jsonwire.AppendQuote(b[:pos], string(b2), &e.Flags)
|
b, err = jsonwire.AppendQuote(b[:pos], string(b2), &e.Flags)
|
||||||
e.unusedCache = b2[:0]
|
e.availBuffer = b2[:0]
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapSyntacticError(e, err, pos, +1)
|
return wrapSyntacticError(e, err, pos, +1)
|
||||||
}
|
}
|
||||||
|
|
@ -900,20 +900,20 @@ func (e *Encoder) OutputOffset() int64 {
|
||||||
return e.s.previousOffsetEnd()
|
return e.s.previousOffsetEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnusedBuffer returns a zero-length buffer with a possible non-zero capacity.
|
// AvailableBuffer returns a zero-length buffer with a possible non-zero capacity.
|
||||||
// This buffer is intended to be used to populate a [Value]
|
// This buffer is intended to be used to populate a [Value]
|
||||||
// being passed to an immediately succeeding [Encoder.WriteValue] call.
|
// being passed to an immediately succeeding [Encoder.WriteValue] call.
|
||||||
//
|
//
|
||||||
// Example usage:
|
// Example usage:
|
||||||
//
|
//
|
||||||
// b := d.UnusedBuffer()
|
// b := d.AvailableBuffer()
|
||||||
// b = append(b, '"')
|
// b = append(b, '"')
|
||||||
// b = appendString(b, v) // append the string formatting of v
|
// b = appendString(b, v) // append the string formatting of v
|
||||||
// b = append(b, '"')
|
// b = append(b, '"')
|
||||||
// ... := d.WriteValue(b)
|
// ... := d.WriteValue(b)
|
||||||
//
|
//
|
||||||
// It is the user's responsibility to ensure that the value is valid JSON.
|
// It is the user's responsibility to ensure that the value is valid JSON.
|
||||||
func (e *Encoder) UnusedBuffer() []byte {
|
func (e *Encoder) AvailableBuffer() []byte {
|
||||||
// NOTE: We don't return e.buf[len(e.buf):cap(e.buf)] since WriteValue would
|
// NOTE: We don't return e.buf[len(e.buf):cap(e.buf)] since WriteValue would
|
||||||
// need to take special care to avoid mangling the data while reformatting.
|
// need to take special care to avoid mangling the data while reformatting.
|
||||||
// WriteValue can't easily identify whether the input Value aliases e.buf
|
// WriteValue can't easily identify whether the input Value aliases e.buf
|
||||||
|
|
@ -921,10 +921,10 @@ func (e *Encoder) UnusedBuffer() []byte {
|
||||||
// Should this ever alias e.buf, we need to consider how it operates with
|
// Should this ever alias e.buf, we need to consider how it operates with
|
||||||
// the specialized performance optimization for bytes.Buffer.
|
// the specialized performance optimization for bytes.Buffer.
|
||||||
n := 1 << bits.Len(uint(e.s.maxValue|63)) // fast approximation for max length
|
n := 1 << bits.Len(uint(e.s.maxValue|63)) // fast approximation for max length
|
||||||
if cap(e.s.unusedCache) < n {
|
if cap(e.s.availBuffer) < n {
|
||||||
e.s.unusedCache = make([]byte, 0, n)
|
e.s.availBuffer = make([]byte, 0, n)
|
||||||
}
|
}
|
||||||
return e.s.unusedCache
|
return e.s.availBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackDepth returns the depth of the state machine for written JSON data.
|
// StackDepth returns the depth of the state machine for written JSON data.
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
|
||||||
mk := newAddressableValue(m.Type().Key())
|
mk := newAddressableValue(m.Type().Key())
|
||||||
mv := newAddressableValue(m.Type().Elem())
|
mv := newAddressableValue(m.Type().Elem())
|
||||||
marshalKey := func(mk addressableValue) error {
|
marshalKey := func(mk addressableValue) error {
|
||||||
b, err := jsonwire.AppendQuote(enc.UnusedBuffer(), mk.String(), &mo.Flags)
|
b, err := jsonwire.AppendQuote(enc.AvailableBuffer(), mk.String(), &mo.Flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newMarshalErrorBefore(enc, m.Type().Key(), err)
|
return newMarshalErrorBefore(enc, m.Type().Key(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ func (n Number) MarshalJSONTo(enc *jsontext.Encoder) error {
|
||||||
}
|
}
|
||||||
n = cmp.Or(n, "0")
|
n = cmp.Or(n, "0")
|
||||||
var num []byte
|
var num []byte
|
||||||
val := enc.UnusedBuffer()
|
val := enc.AvailableBuffer()
|
||||||
if stringify {
|
if stringify {
|
||||||
val = append(val, '"')
|
val = append(val, '"')
|
||||||
val = append(val, n...)
|
val = append(val, n...)
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ type Options = jsonopts.Options
|
||||||
// - [jsontext.AllowInvalidUTF8]
|
// - [jsontext.AllowInvalidUTF8]
|
||||||
// - [jsontext.EscapeForHTML]
|
// - [jsontext.EscapeForHTML]
|
||||||
// - [jsontext.EscapeForJS]
|
// - [jsontext.EscapeForJS]
|
||||||
// - [jsontext.PreserveRawString]
|
// - [jsontext.PreserveRawStrings]
|
||||||
//
|
//
|
||||||
// All other boolean options are set to false.
|
// All other boolean options are set to false.
|
||||||
// All non-boolean options are set to the zero value,
|
// All non-boolean options are set to the zero value,
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,14 @@ type Hash64 interface {
|
||||||
Sum64() uint64
|
Sum64() uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Cloner is a hash function whose state can be cloned.
|
// A Cloner is a hash function whose state can be cloned, returning a value with
|
||||||
|
// equivalent and independent state.
|
||||||
//
|
//
|
||||||
// All [Hash] implementations in the standard library implement this interface,
|
// All [Hash] implementations in the standard library implement this interface,
|
||||||
// unless GOFIPS140=v1.0.0 is set.
|
// unless GOFIPS140=v1.0.0 is set.
|
||||||
//
|
//
|
||||||
// If a hash can only determine at runtime if it can be cloned,
|
// If a hash can only determine at runtime if it can be cloned (e.g. if it wraps
|
||||||
// (e.g., if it wraps another hash), it may return [errors.ErrUnsupported].
|
// another hash), it may return an error wrapping [errors.ErrUnsupported].
|
||||||
type Cloner interface {
|
type Cloner interface {
|
||||||
Hash
|
Hash
|
||||||
Clone() (Cloner, error)
|
Clone() (Cloner, error)
|
||||||
|
|
|
||||||
|
|
@ -654,6 +654,17 @@ func TestWaitGroupInBubble(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://go.dev/issue/74386
|
||||||
|
func TestWaitGroupRacingAdds(t *testing.T) {
|
||||||
|
synctest.Run(func() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for range 100 {
|
||||||
|
wg.Go(func() {})
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestWaitGroupOutOfBubble(t *testing.T) {
|
func TestWaitGroupOutOfBubble(t *testing.T) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
@ -705,29 +716,35 @@ func TestWaitGroupMovedBetweenBubblesWithNonZeroCount(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitGroupMovedBetweenBubblesWithZeroCount(t *testing.T) {
|
func TestWaitGroupDisassociateInWait(t *testing.T) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
synctest.Run(func() {
|
synctest.Run(func() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
|
// Count and waiters are 0, so Wait disassociates the WaitGroup.
|
||||||
|
wg.Wait()
|
||||||
})
|
})
|
||||||
synctest.Run(func() {
|
synctest.Run(func() {
|
||||||
// Reusing the WaitGroup is safe, because its count is zero.
|
// Reusing the WaitGroup is safe, because it is no longer bubbled.
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitGroupMovedBetweenBubblesAfterWait(t *testing.T) {
|
func TestWaitGroupDisassociateInAdd(t *testing.T) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
synctest.Run(func() {
|
synctest.Run(func() {
|
||||||
wg.Go(func() {})
|
wg.Add(1)
|
||||||
wg.Wait()
|
go wg.Wait()
|
||||||
|
synctest.Wait() // wait for Wait to block
|
||||||
|
// Count is 0 and waiters != 0, so Done wakes the waiters and
|
||||||
|
// disassociates the WaitGroup.
|
||||||
|
wg.Done()
|
||||||
})
|
})
|
||||||
synctest.Run(func() {
|
synctest.Run(func() {
|
||||||
// Reusing the WaitGroup is safe, because its count is zero.
|
// Reusing the WaitGroup is safe, because it is no longer bubbled.
|
||||||
wg.Go(func() {})
|
wg.Add(1)
|
||||||
wg.Wait()
|
wg.Done()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
src/internal/trace/testdata/testprog/stacks.go
vendored
14
src/internal/trace/testdata/testprog/stacks.go
vendored
|
|
@ -97,6 +97,11 @@ func main() {
|
||||||
rp.Read(data[:])
|
rp.Read(data[:])
|
||||||
pipeReadDone <- true
|
pipeReadDone <- true
|
||||||
}()
|
}()
|
||||||
|
go func() { // func12
|
||||||
|
for {
|
||||||
|
syncPreemptPoint()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
|
@ -127,3 +132,12 @@ func main() {
|
||||||
|
|
||||||
runtime.GOMAXPROCS(oldGoMaxProcs)
|
runtime.GOMAXPROCS(oldGoMaxProcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func syncPreemptPoint() {
|
||||||
|
if never {
|
||||||
|
syncPreemptPoint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var never bool
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,8 @@ func TestTraceStacks(t *testing.T) {
|
||||||
const mainLine = 21
|
const mainLine = 21
|
||||||
want := []evDesc{
|
want := []evDesc{
|
||||||
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
|
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
|
||||||
{"main.main", mainLine + 82},
|
{"runtime.Gosched", 0},
|
||||||
|
{"main.main", mainLine + 87},
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{
|
{trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{
|
||||||
{"main.main", mainLine + 11},
|
{"main.main", mainLine + 11},
|
||||||
|
|
@ -349,7 +350,7 @@ func TestTraceStacks(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||||
{"runtime.chansend1", 0},
|
{"runtime.chansend1", 0},
|
||||||
{"main.main", mainLine + 84},
|
{"main.main", mainLine + 89},
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||||
{"runtime.chansend1", 0},
|
{"runtime.chansend1", 0},
|
||||||
|
|
@ -357,7 +358,7 @@ func TestTraceStacks(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||||
{"runtime.chanrecv1", 0},
|
{"runtime.chanrecv1", 0},
|
||||||
{"main.main", mainLine + 85},
|
{"main.main", mainLine + 90},
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||||
{"runtime.selectgo", 0},
|
{"runtime.selectgo", 0},
|
||||||
|
|
@ -365,7 +366,7 @@ func TestTraceStacks(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||||
{"runtime.selectgo", 0},
|
{"runtime.selectgo", 0},
|
||||||
{"main.main", mainLine + 86},
|
{"main.main", mainLine + 91},
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||||
{"sync.(*Mutex).Lock", 0},
|
{"sync.(*Mutex).Lock", 0},
|
||||||
|
|
@ -382,7 +383,7 @@ func TestTraceStacks(t *testing.T) {
|
||||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||||
{"sync.(*WaitGroup).Add", 0},
|
{"sync.(*WaitGroup).Add", 0},
|
||||||
{"sync.(*WaitGroup).Done", 0},
|
{"sync.(*WaitGroup).Done", 0},
|
||||||
{"main.main", mainLine + 91},
|
{"main.main", mainLine + 96},
|
||||||
}},
|
}},
|
||||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||||
{"sync.(*Cond).Wait", 0},
|
{"sync.(*Cond).Wait", 0},
|
||||||
|
|
@ -402,6 +403,10 @@ func TestTraceStacks(t *testing.T) {
|
||||||
{"runtime.GOMAXPROCS", 0},
|
{"runtime.GOMAXPROCS", 0},
|
||||||
{"main.main", 0},
|
{"main.main", 0},
|
||||||
}},
|
}},
|
||||||
|
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
|
||||||
|
{"main.syncPreemptPoint", 0},
|
||||||
|
{"main.main.func12", 0},
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
if !stress {
|
if !stress {
|
||||||
// Only check for this stack if !stress because traceAdvance alone could
|
// Only check for this stack if !stress because traceAdvance alone could
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ with the extra operations and then provide an iterator over positions.
|
||||||
For example, a tree implementation might provide:
|
For example, a tree implementation might provide:
|
||||||
|
|
||||||
// Positions returns an iterator over positions in the sequence.
|
// Positions returns an iterator over positions in the sequence.
|
||||||
func (t *Tree[V]) Positions() iter.Seq[*Pos]
|
func (t *Tree[V]) Positions() iter.Seq[*Pos[V]]
|
||||||
|
|
||||||
// A Pos represents a position in the sequence.
|
// A Pos represents a position in the sequence.
|
||||||
// It is only valid during the yield call it is passed to.
|
// It is only valid during the yield call it is passed to.
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,14 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
|
||||||
if !ac.IsUndefined() {
|
if !ac.IsUndefined() {
|
||||||
// Abort the Fetch request.
|
// Abort the Fetch request.
|
||||||
ac.Call("abort")
|
ac.Call("abort")
|
||||||
|
|
||||||
|
// Wait for fetch promise to be rejected prior to exiting. See
|
||||||
|
// https://github.com/golang/go/issues/57098 for more details.
|
||||||
|
select {
|
||||||
|
case resp := <-respCh:
|
||||||
|
resp.Body.Close()
|
||||||
|
case <-errCh:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, req.Context().Err()
|
return nil, req.Context().Err()
|
||||||
case resp := <-respCh:
|
case resp := <-respCh:
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ package os
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -34,7 +35,15 @@ func removeAll(path string) error {
|
||||||
// its parent directory
|
// its parent directory
|
||||||
parentDir, base := splitPath(path)
|
parentDir, base := splitPath(path)
|
||||||
|
|
||||||
parent, err := Open(parentDir)
|
flag := O_RDONLY
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// On Windows, the process might not have read permission on the parent directory,
|
||||||
|
// but still can delete files in it. See https://go.dev/issue/74134.
|
||||||
|
// We can open a file even if we don't have read permission by passing the
|
||||||
|
// O_WRONLY | O_RDWR flag, which is mapped to FILE_READ_ATTRIBUTES.
|
||||||
|
flag = O_WRONLY | O_RDWR
|
||||||
|
}
|
||||||
|
parent, err := OpenFile(parentDir, flag, 0)
|
||||||
if IsNotExist(err) {
|
if IsNotExist(err) {
|
||||||
// If parent does not exist, base cannot exist. Fail silently
|
// If parent does not exist, base cannot exist. Fail silently
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -8719,6 +8719,11 @@ func TestTypeAssert(t *testing.T) {
|
||||||
testTypeAssert(t, any(int(1)), int(1), true)
|
testTypeAssert(t, any(int(1)), int(1), true)
|
||||||
testTypeAssert(t, any(int(1)), byte(0), false)
|
testTypeAssert(t, any(int(1)), byte(0), false)
|
||||||
testTypeAssert(t, fmt.Stringer(vv), vv, true)
|
testTypeAssert(t, fmt.Stringer(vv), vv, true)
|
||||||
|
|
||||||
|
testTypeAssert(t, any(nil), any(nil), false)
|
||||||
|
testTypeAssert(t, any(nil), error(nil), false)
|
||||||
|
testTypeAssert(t, error(nil), any(nil), false)
|
||||||
|
testTypeAssert(t, error(nil), error(nil), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTypeAssert[T comparable, V any](t *testing.T, val V, wantVal T, wantOk bool) {
|
func testTypeAssert[T comparable, V any](t *testing.T, val V, wantVal T, wantOk bool) {
|
||||||
|
|
|
||||||
|
|
@ -1514,16 +1514,8 @@ func TypeAssert[T any](v Value) (T, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := abi.TypeFor[T]()
|
typ := abi.TypeFor[T]()
|
||||||
if typ != v.typ() {
|
|
||||||
// We can't just return false here:
|
|
||||||
//
|
|
||||||
// var zero T
|
|
||||||
// return zero, false
|
|
||||||
//
|
|
||||||
// since this function should work in the same manner as v.Interface().(T) does.
|
|
||||||
// Thus we have to handle two cases specially.
|
|
||||||
|
|
||||||
// Return the element inside the interface.
|
// If v is an interface, return the element inside the interface.
|
||||||
//
|
//
|
||||||
// T is a concrete type and v is an interface. For example:
|
// T is a concrete type and v is an interface. For example:
|
||||||
//
|
//
|
||||||
|
|
@ -1531,17 +1523,23 @@ func TypeAssert[T any](v Value) (T, bool) {
|
||||||
// val := ValueOf(&v).Elem()
|
// val := ValueOf(&v).Elem()
|
||||||
// TypeAssert[int](val) == val.Interface().(int)
|
// TypeAssert[int](val) == val.Interface().(int)
|
||||||
//
|
//
|
||||||
// T is a interface and v is an interface, but the iface types are different. For example:
|
// T is a interface and v is a non-nil interface value. For example:
|
||||||
//
|
//
|
||||||
// var v any = &someError{}
|
// var v any = &someError{}
|
||||||
// val := ValueOf(&v).Elem()
|
// val := ValueOf(&v).Elem()
|
||||||
// TypeAssert[error](val) == val.Interface().(error)
|
// TypeAssert[error](val) == val.Interface().(error)
|
||||||
|
//
|
||||||
|
// T is a interface and v is a nil interface value. For example:
|
||||||
|
//
|
||||||
|
// var v error = nil
|
||||||
|
// val := ValueOf(&v).Elem()
|
||||||
|
// TypeAssert[error](val) == val.Interface().(error)
|
||||||
if v.kind() == Interface {
|
if v.kind() == Interface {
|
||||||
v, ok := packIfaceValueIntoEmptyIface(v).(T)
|
v, ok := packIfaceValueIntoEmptyIface(v).(T)
|
||||||
return v, ok
|
return v, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// T is an interface, v is a concrete type. For example:
|
// If T is an interface and v is a concrete type. For example:
|
||||||
//
|
//
|
||||||
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
|
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
|
||||||
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
|
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
|
||||||
|
|
@ -1550,10 +1548,12 @@ func TypeAssert[T any](v Value) (T, bool) {
|
||||||
return v, ok
|
return v, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Both v and T must be concrete types.
|
||||||
|
// The only way for an type-assertion to match is if the types are equal.
|
||||||
|
if typ != v.typ() {
|
||||||
var zero T
|
var zero T
|
||||||
return zero, false
|
return zero, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.flag&flagIndir == 0 {
|
if v.flag&flagIndir == 0 {
|
||||||
return *(*T)(unsafe.Pointer(&v.ptr)), true
|
return *(*T)(unsafe.Pointer(&v.ptr)), true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1059,6 +1059,28 @@ func (mp *m) becomeSpinning() {
|
||||||
sched.needspinning.Store(0)
|
sched.needspinning.Store(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Take a snapshot of allp, for use after dropping the P.
|
||||||
|
//
|
||||||
|
// Must be called with a P, but the returned slice may be used after dropping
|
||||||
|
// the P. The M holds a reference on the snapshot to keep the backing array
|
||||||
|
// alive.
|
||||||
|
//
|
||||||
|
//go:yeswritebarrierrec
|
||||||
|
func (mp *m) snapshotAllp() []*p {
|
||||||
|
mp.allpSnapshot = allp
|
||||||
|
return mp.allpSnapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the saved allp snapshot. Should be called as soon as the snapshot is
|
||||||
|
// no longer required.
|
||||||
|
//
|
||||||
|
// Must be called after reacquiring a P, as it requires a write barrier.
|
||||||
|
//
|
||||||
|
//go:yeswritebarrierrec
|
||||||
|
func (mp *m) clearAllpSnapshot() {
|
||||||
|
mp.allpSnapshot = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mp *m) hasCgoOnStack() bool {
|
func (mp *m) hasCgoOnStack() bool {
|
||||||
return mp.ncgo > 0 || mp.isextra
|
return mp.ncgo > 0 || mp.isextra
|
||||||
}
|
}
|
||||||
|
|
@ -3307,10 +3329,10 @@ func execute(gp *g, inheritTime bool) {
|
||||||
tryRecordGoroutineProfile(gp, nil, osyield)
|
tryRecordGoroutineProfile(gp, nil, osyield)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign gp.m before entering _Grunning so running Gs have an
|
// Assign gp.m before entering _Grunning so running Gs have an M.
|
||||||
// M.
|
|
||||||
mp.curg = gp
|
mp.curg = gp
|
||||||
gp.m = mp
|
gp.m = mp
|
||||||
|
gp.syncSafePoint = false // Clear the flag, which may have been set by morestack.
|
||||||
casgstatus(gp, _Grunnable, _Grunning)
|
casgstatus(gp, _Grunnable, _Grunning)
|
||||||
gp.waitsince = 0
|
gp.waitsince = 0
|
||||||
gp.preempt = false
|
gp.preempt = false
|
||||||
|
|
@ -3346,6 +3368,11 @@ func findRunnable() (gp *g, inheritTime, tryWakeP bool) {
|
||||||
// an M.
|
// an M.
|
||||||
|
|
||||||
top:
|
top:
|
||||||
|
// We may have collected an allp snapshot below. The snapshot is only
|
||||||
|
// required in each loop iteration. Clear it to all GC to collect the
|
||||||
|
// slice.
|
||||||
|
mp.clearAllpSnapshot()
|
||||||
|
|
||||||
pp := mp.p.ptr()
|
pp := mp.p.ptr()
|
||||||
if sched.gcwaiting.Load() {
|
if sched.gcwaiting.Load() {
|
||||||
gcstopm()
|
gcstopm()
|
||||||
|
|
@ -3527,7 +3554,11 @@ top:
|
||||||
// which can change underfoot once we no longer block
|
// which can change underfoot once we no longer block
|
||||||
// safe-points. We don't need to snapshot the contents because
|
// safe-points. We don't need to snapshot the contents because
|
||||||
// everything up to cap(allp) is immutable.
|
// everything up to cap(allp) is immutable.
|
||||||
allpSnapshot := allp
|
//
|
||||||
|
// We clear the snapshot from the M after return via
|
||||||
|
// mp.clearAllpSnapshop (in schedule) and on each iteration of the top
|
||||||
|
// loop.
|
||||||
|
allpSnapshot := mp.snapshotAllp()
|
||||||
// Also snapshot masks. Value changes are OK, but we can't allow
|
// Also snapshot masks. Value changes are OK, but we can't allow
|
||||||
// len to change out from under us.
|
// len to change out from under us.
|
||||||
idlepMaskSnapshot := idlepMask
|
idlepMaskSnapshot := idlepMask
|
||||||
|
|
@ -3668,6 +3699,9 @@ top:
|
||||||
pollUntil = checkTimersNoP(allpSnapshot, timerpMaskSnapshot, pollUntil)
|
pollUntil = checkTimersNoP(allpSnapshot, timerpMaskSnapshot, pollUntil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't need allp anymore at this pointer, but can't clear the
|
||||||
|
// snapshot without a P for the write barrier..
|
||||||
|
|
||||||
// Poll network until next timer.
|
// Poll network until next timer.
|
||||||
if netpollinited() && (netpollAnyWaiters() || pollUntil != 0) && sched.lastpoll.Swap(0) != 0 {
|
if netpollinited() && (netpollAnyWaiters() || pollUntil != 0) && sched.lastpoll.Swap(0) != 0 {
|
||||||
sched.pollUntil.Store(pollUntil)
|
sched.pollUntil.Store(pollUntil)
|
||||||
|
|
@ -4103,6 +4137,11 @@ top:
|
||||||
|
|
||||||
gp, inheritTime, tryWakeP := findRunnable() // blocks until work is available
|
gp, inheritTime, tryWakeP := findRunnable() // blocks until work is available
|
||||||
|
|
||||||
|
// findRunnable may have collected an allp snapshot. The snapshot is
|
||||||
|
// only required within findRunnable. Clear it to all GC to collect the
|
||||||
|
// slice.
|
||||||
|
mp.clearAllpSnapshot()
|
||||||
|
|
||||||
if debug.dontfreezetheworld > 0 && freezing.Load() {
|
if debug.dontfreezetheworld > 0 && freezing.Load() {
|
||||||
// See comment in freezetheworld. We don't want to perturb
|
// See comment in freezetheworld. We don't want to perturb
|
||||||
// scheduler state, so we didn't gcstopm in findRunnable, but
|
// scheduler state, so we didn't gcstopm in findRunnable, but
|
||||||
|
|
|
||||||
|
|
@ -528,11 +528,12 @@ func TestGdbBacktrace(t *testing.T) {
|
||||||
got, err := cmd.CombinedOutput()
|
got, err := cmd.CombinedOutput()
|
||||||
t.Logf("gdb output:\n%s", got)
|
t.Logf("gdb output:\n%s", got)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
noProcessRE := regexp.MustCompile(`Couldn't get [a-zA-Z_ -]* ?registers: No such process\.`)
|
||||||
switch {
|
switch {
|
||||||
case bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")):
|
case bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")):
|
||||||
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28551
|
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28551
|
||||||
testenv.SkipFlaky(t, 43068)
|
testenv.SkipFlaky(t, 43068)
|
||||||
case bytes.Contains(got, []byte("Couldn't get registers: No such process.")),
|
case noProcessRE.Match(got),
|
||||||
bytes.Contains(got, []byte("Unable to fetch general registers.: No such process.")),
|
bytes.Contains(got, []byte("Unable to fetch general registers.: No such process.")),
|
||||||
bytes.Contains(got, []byte("reading register pc (#64): No such process.")):
|
bytes.Contains(got, []byte("reading register pc (#64): No such process.")):
|
||||||
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=9086
|
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=9086
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,7 @@ type g struct {
|
||||||
runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking
|
runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking
|
||||||
lockedm muintptr
|
lockedm muintptr
|
||||||
fipsIndicator uint8
|
fipsIndicator uint8
|
||||||
|
syncSafePoint bool // set if g is stopped at a synchronous safe point.
|
||||||
runningCleanups atomic.Bool
|
runningCleanups atomic.Bool
|
||||||
sig uint32
|
sig uint32
|
||||||
writebuf []byte
|
writebuf []byte
|
||||||
|
|
@ -568,6 +569,7 @@ type m struct {
|
||||||
needextram bool
|
needextram bool
|
||||||
g0StackAccurate bool // whether the g0 stack has accurate bounds
|
g0StackAccurate bool // whether the g0 stack has accurate bounds
|
||||||
traceback uint8
|
traceback uint8
|
||||||
|
allpSnapshot []*p // Snapshot of allp for use after dropping P in findRunnable, nil otherwise.
|
||||||
ncgocall uint64 // number of cgo calls in total
|
ncgocall uint64 // number of cgo calls in total
|
||||||
ncgo int32 // number of cgo calls currently in progress
|
ncgo int32 // number of cgo calls currently in progress
|
||||||
cgoCallersUse atomic.Uint32 // if non-zero, cgoCallers in use temporarily
|
cgoCallersUse atomic.Uint32 // if non-zero, cgoCallers in use temporarily
|
||||||
|
|
|
||||||
|
|
@ -1115,6 +1115,9 @@ func newstack() {
|
||||||
shrinkstack(gp)
|
shrinkstack(gp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set a flag indicated that we've been synchronously preempted.
|
||||||
|
gp.syncSafePoint = true
|
||||||
|
|
||||||
if gp.preemptStop {
|
if gp.preemptStop {
|
||||||
preemptPark(gp) // never returns
|
preemptPark(gp) // never returns
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,6 @@ import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -84,12 +82,8 @@ func GoNeedM() {
|
||||||
|
|
||||||
func NeedmDeadlock() {
|
func NeedmDeadlock() {
|
||||||
// The failure symptom is that the program hangs because of a
|
// The failure symptom is that the program hangs because of a
|
||||||
// deadlock in needm, so set an alarm.
|
// deadlock in needm. Instead of using an arbitrary timeout,
|
||||||
go func() {
|
// we let the test deadline expire if it deadlocks.
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
fmt.Println("Hung for 5 seconds")
|
|
||||||
os.Exit(1)
|
|
||||||
}()
|
|
||||||
|
|
||||||
C.runNeedmSignalThread()
|
C.runNeedmSignalThread()
|
||||||
fmt.Println("OK")
|
fmt.Println("OK")
|
||||||
|
|
|
||||||
|
|
@ -457,7 +457,7 @@ func (tl traceLocker) GoPreempt() {
|
||||||
|
|
||||||
// GoStop emits a GoStop event with the provided reason.
|
// GoStop emits a GoStop event with the provided reason.
|
||||||
func (tl traceLocker) GoStop(reason traceGoStopReason) {
|
func (tl traceLocker) GoStop(reason traceGoStopReason) {
|
||||||
tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1))
|
tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoPark emits a GoBlock event with the provided reason.
|
// GoPark emits a GoBlock event with the provided reason.
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,25 @@ func traceStack(skip int, gp *g, gen uintptr) uint64 {
|
||||||
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.syscallbp), pcBuf[2:])
|
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.syscallbp), pcBuf[2:])
|
||||||
} else {
|
} else {
|
||||||
pcBuf[1] = gp.sched.pc
|
pcBuf[1] = gp.sched.pc
|
||||||
|
if gp.syncSafePoint {
|
||||||
|
// We're stopped in morestack, which is an odd state because gp.sched.bp
|
||||||
|
// refers to our parent frame, since we haven't had the chance to push our
|
||||||
|
// frame pointer to the stack yet. If we just start walking from gp.sched.bp,
|
||||||
|
// we'll skip a frame as a result. Luckily, we can find the PC we want right
|
||||||
|
// at gp.sched.sp on non-LR platforms, and we have it directly on LR platforms.
|
||||||
|
// See issue go.dev/issue/68090.
|
||||||
|
if usesLR {
|
||||||
|
pcBuf[2] = gp.sched.lr
|
||||||
|
} else {
|
||||||
|
pcBuf[2] = *(*uintptr)(unsafe.Pointer(gp.sched.sp))
|
||||||
|
}
|
||||||
|
nstk += 2 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[3:])
|
||||||
|
} else {
|
||||||
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[2:])
|
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[2:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if nstk > 0 {
|
if nstk > 0 {
|
||||||
nstk-- // skip runtime.goexit
|
nstk-- // skip runtime.goexit
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,13 +120,6 @@ func (wg *WaitGroup) Add(delta int) {
|
||||||
if w != 0 && delta > 0 && v == int32(delta) {
|
if w != 0 && delta > 0 && v == int32(delta) {
|
||||||
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
|
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
|
||||||
}
|
}
|
||||||
if v == 0 && bubbled {
|
|
||||||
// Disassociate the WaitGroup from its bubble.
|
|
||||||
synctest.Disassociate(wg)
|
|
||||||
if w == 0 {
|
|
||||||
wg.state.Store(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v > 0 || w == 0 {
|
if v > 0 || w == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +133,11 @@ func (wg *WaitGroup) Add(delta int) {
|
||||||
}
|
}
|
||||||
// Reset waiters count to 0.
|
// Reset waiters count to 0.
|
||||||
wg.state.Store(0)
|
wg.state.Store(0)
|
||||||
|
if bubbled {
|
||||||
|
// Adds must not happen concurrently with wait when counter is 0,
|
||||||
|
// so we can safely disassociate wg from its current bubble.
|
||||||
|
synctest.Disassociate(wg)
|
||||||
|
}
|
||||||
for ; w != 0; w-- {
|
for ; w != 0; w-- {
|
||||||
runtime_Semrelease(&wg.sema, false, 0)
|
runtime_Semrelease(&wg.sema, false, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -166,13 +164,20 @@ func (wg *WaitGroup) Wait() {
|
||||||
for {
|
for {
|
||||||
state := wg.state.Load()
|
state := wg.state.Load()
|
||||||
v := int32(state >> 32)
|
v := int32(state >> 32)
|
||||||
w := uint32(state)
|
w := uint32(state & 0x7fffffff)
|
||||||
if v == 0 {
|
if v == 0 {
|
||||||
// Counter is 0, no need to wait.
|
// Counter is 0, no need to wait.
|
||||||
if race.Enabled {
|
if race.Enabled {
|
||||||
race.Enable()
|
race.Enable()
|
||||||
race.Acquire(unsafe.Pointer(wg))
|
race.Acquire(unsafe.Pointer(wg))
|
||||||
}
|
}
|
||||||
|
if w == 0 && state&waitGroupBubbleFlag != 0 && synctest.IsAssociated(wg) {
|
||||||
|
// Adds must not happen concurrently with wait when counter is 0,
|
||||||
|
// so we can disassociate wg from its current bubble.
|
||||||
|
if wg.state.CompareAndSwap(state, 0) {
|
||||||
|
synctest.Disassociate(wg)
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Increment waiters count.
|
// Increment waiters count.
|
||||||
|
|
|
||||||
30
test/fixedbugs/issue74379.go
Normal file
30
test/fixedbugs/issue74379.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func crashOnErr(err error) bool {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
fmt.Println("failed to have expected panic")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fmt.Println(crashOnErr(errors.New("test error")))
|
||||||
|
}
|
||||||
32
test/fixedbugs/issue74379b.go
Normal file
32
test/fixedbugs/issue74379b.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func crashOnErr(err error) int {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
fmt.Println("failed to have expected panic")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s := make([]int, crashOnErr(errors.New("test error")))
|
||||||
|
println("unreachable: len(s) =", len(s))
|
||||||
|
}
|
||||||
54
test/fixedbugs/issue74379c.go
Normal file
54
test/fixedbugs/issue74379c.go
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type S struct{ a, b int }
|
||||||
|
|
||||||
|
func crashOnErr1(err error) S {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return S{} // zero value struct
|
||||||
|
}
|
||||||
|
|
||||||
|
func f1() {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
fmt.Println("failed to have expected panic")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fmt.Println(crashOnErr1(errors.New("test error")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func crashOnErr2(err error) S {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return S{1, 2} // not zero value struct
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2() {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
fmt.Println("failed to have expected panic")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fmt.Println(crashOnErr2(errors.New("test error")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f1()
|
||||||
|
f2()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue