[dev.simd] all: merge master (740857f) into dev.simd

Merge List:

+ 2025-06-30 740857f529 runtime: stash allpSnapshot on the M
+ 2025-06-30 9ae38be302 sync: disassociate WaitGroups from bubbles on Wait
+ 2025-06-30 4731832342 crypto/hmac: wrap ErrUnsupported returned by Clone
+ 2025-06-30 03ad694dcb runtime: update skips for TestGdbBacktrace
+ 2025-06-30 9d1cd0b881 iter: add missing type parameter in doc
+ 2025-06-29 acb914f2c2 cmd/doc: fix -http on Windows
+ 2025-06-27 b51f1cdb87 runtime: remove arbitrary 5-second timeout in TestNeedmDeadlock
+ 2025-06-27 f1e6ae2f6f reflect: fix TypeAssert on nil interface values
+ 2025-06-27 e81c624656 os: use minimal file permissions when opening parent directory in RemoveAll
+ 2025-06-27 2a22aefa1f encoding/json: add security section to doc
+ 2025-06-27 742fda9524 runtime: account for missing frame pointer in preamble
+ 2025-06-27 fdc076ce76 net/http: fix RoundTrip context cancellation for js/wasm
+ 2025-06-27 d9d2cadd63 encoding/json: fix typo in hotlink for jsontext.PreserveRawStrings
+ 2025-06-26 0f8ab2db17 cmd/link: permit a larger size BSS reference to a smaller DATA symbol
+ 2025-06-26 988a20c8c5 cmd/compile/internal/escape: evaluate any side effects when rewriting with literals
+ 2025-06-25 b5d555991a encoding/json/jsontext: remove Encoder.UnusedBuffer
+ 2025-06-25 0b4d2eab2f encoding/json/jsontext: rename Encoder.UnusedBuffer as Encoder.AvailableBuffer

Change-Id: Iea44ab825bdf087fbe7570df8d2d66d1d3327c31
This commit is contained in:
Cherry Mui 2025-06-30 15:10:56 -04:00
commit f849225b3b
39 changed files with 537 additions and 139 deletions

View file

@ -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 {

View file

@ -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.

View 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

View 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)
}

View 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

View 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")
}
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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")

View file

@ -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
} }

View file

@ -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:

View file

@ -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 (

View file

@ -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.

View file

@ -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)
} }

View file

@ -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...)

View file

@ -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,

View file

@ -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)

View file

@ -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()
}) })
} }

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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:

View file

@ -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

View file

@ -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) {

View file

@ -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
} }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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")

View file

@ -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.

View file

@ -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
} }

View file

@ -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.

View 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")))
}

View 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))
}

View 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()
}