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_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"},
|
||||
{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"}},
|
||||
}
|
||||
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.
|
||||
func srcPath(path string) string {
|
||||
return filepath.Join("testdata", path)
|
||||
return "./testdata/" + path
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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() {
|
||||
case ir.OMAKESLICE:
|
||||
// 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
|
||||
}
|
||||
|
||||
if (*r).Op() != ir.OLITERAL {
|
||||
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
|
||||
lit, ok := s.(*ir.BasicLit)
|
||||
if !ok || lit.Val().Kind() != constant.Int {
|
||||
base.Fatalf("unexpected BasicLit Kind")
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
case ir.OCONVIFACE:
|
||||
// Check if we can replace a non-constant expression in an interface conversion with
|
||||
// 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 {
|
||||
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)
|
||||
conv.X = ir.NewBasicLit(conv.X.Pos(), conv.X.Type(), v.Val())
|
||||
typecheck.Expr(conv)
|
||||
|
|
|
|||
|
|
@ -405,6 +405,8 @@
|
|||
//
|
||||
// go doc
|
||||
// Show documentation for current package.
|
||||
// go doc -http
|
||||
// Serve HTML documentation over HTTP for the current package.
|
||||
// go doc Foo
|
||||
// Show documentation for Foo in the current package.
|
||||
// (Foo starts with a capital letter so it cannot match
|
||||
|
|
@ -447,6 +449,8 @@
|
|||
// Treat a command (package main) like a regular package.
|
||||
// Otherwise package main's exported symbols are hidden
|
||||
// when showing the package's top-level documentation.
|
||||
// -http
|
||||
// Serve HTML docs over HTTP.
|
||||
// -short
|
||||
// One-line representation for each symbol.
|
||||
// -src
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ different cases. If this occurs, documentation for all matches is printed.
|
|||
Examples:
|
||||
go doc
|
||||
Show documentation for current package.
|
||||
go doc -http
|
||||
Serve HTML documentation over HTTP for the current package.
|
||||
go doc Foo
|
||||
Show documentation for Foo in the current package.
|
||||
(Foo starts with a capital letter so it cannot match
|
||||
|
|
@ -116,6 +118,8 @@ Flags:
|
|||
Treat a command (package main) like a regular package.
|
||||
Otherwise package main's exported symbols are hidden
|
||||
when showing the package's top-level documentation.
|
||||
-http
|
||||
Serve HTML docs over HTTP.
|
||||
-short
|
||||
One-line representation for each symbol.
|
||||
-src
|
||||
|
|
|
|||
|
|
@ -227,8 +227,16 @@ func doPkgsite(urlPath string) error {
|
|||
fields := strings.Fields(vars)
|
||||
if err == nil && len(fields) == 2 {
|
||||
goproxy, gomodcache := fields[0], fields[1]
|
||||
goproxy = "file://" + filepath.Join(gomodcache, "cache", "download") + "," + goproxy
|
||||
env = append(env, "GOPROXY="+goproxy)
|
||||
gomodcache = filepath.Join(gomodcache, "cache", "download")
|
||||
// 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"
|
||||
|
|
|
|||
|
|
@ -253,6 +253,12 @@ type Loader struct {
|
|||
|
||||
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
|
||||
|
||||
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
|
||||
//
|
||||
// new sym old sym result
|
||||
// ---------------------------------------------
|
||||
// -------------------------------------------------------
|
||||
// TEXT BSS new wins
|
||||
// DATA DATA ERROR
|
||||
// DATA lg/eq BSS sm/eq new wins
|
||||
// DATA small BSS large ERROR
|
||||
// BSS large DATA small ERROR
|
||||
// DATA small BSS large merge: new with larger size
|
||||
// BSS large DATA small merge: old with larger size
|
||||
// BSS large BSS small new wins
|
||||
// BSS sm/eq D/B lg/eq old wins
|
||||
// BSS TEXT old wins
|
||||
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
|
||||
newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
|
||||
oldIsText := oldtyp.IsText()
|
||||
newIsText := newtyp.IsText()
|
||||
oldHasContent := oldr.DataSize(oldli) != 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
|
||||
switch {
|
||||
case newIsText && oldIsBSS,
|
||||
newHasContent && oldIsBSS && sz >= oldsz,
|
||||
newHasContent && oldIsBSS,
|
||||
newIsBSS && oldIsBSS && sz > oldsz:
|
||||
// new symbol overwrites old symbol.
|
||||
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.
|
||||
if sz > oldsz {
|
||||
// See the comment above for sizeFixups.
|
||||
l.sizeFixups = append(l.sizeFixups, symAndSize{oldi, uint32(sz)})
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
@ -2277,6 +2298,10 @@ func (l *Loader) LoadSyms(arch *sys.Arch) {
|
|||
st.preloadSyms(r, hashedDef)
|
||||
st.preloadSyms(r, nonPkgDef)
|
||||
}
|
||||
for _, sf := range l.sizeFixups {
|
||||
pp := l.cloneToExternal(sf.sym)
|
||||
pp.size = int64(sf.size)
|
||||
}
|
||||
for _, vr := range st.linknameVarRefs {
|
||||
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
|
||||
// easier to do this if we make the updates to an external symbol
|
||||
// payload.
|
||||
func (l *Loader) cloneToExternal(symIdx Sym) {
|
||||
func (l *Loader) cloneToExternal(symIdx Sym) *extSymPayload {
|
||||
if l.IsExternal(symIdx) {
|
||||
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.
|
||||
l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
|
||||
l.SetAttrShared(symIdx, r.Shared())
|
||||
|
||||
return pp
|
||||
}
|
||||
|
||||
// Copy the payload of symbol src to dst. Both src and dst must be external
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"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) {
|
||||
if boring.Enabled {
|
||||
t.Skip("hash.Hash provided by boringcrypto are not comparable")
|
||||
|
|
|
|||
|
|
@ -130,26 +130,36 @@ func (h *HMAC) Reset() {
|
|||
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.
|
||||
// Otherwise, it returns [errors.ErrUnsupported].
|
||||
// Otherwise, it returns an error wrapping [errors.ErrUnsupported].
|
||||
func (h *HMAC) Clone() (hash.Cloner, error) {
|
||||
r := *h
|
||||
ic, ok := h.inner.(hash.Cloner)
|
||||
if !ok {
|
||||
return nil, errors.ErrUnsupported
|
||||
return nil, errCloneUnsupported{}
|
||||
}
|
||||
oc, ok := h.outer.(hash.Cloner)
|
||||
if !ok {
|
||||
return nil, errors.ErrUnsupported
|
||||
return nil, errCloneUnsupported{}
|
||||
}
|
||||
var err error
|
||||
r.inner, err = ic.Clone()
|
||||
if err != nil {
|
||||
return nil, errors.ErrUnsupported
|
||||
return nil, errCloneUnsupported{}
|
||||
}
|
||||
r.outer, err = oc.Clone()
|
||||
if err != nil {
|
||||
return nil, errors.ErrUnsupported
|
||||
return nil, errCloneUnsupported{}
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,11 +43,14 @@ import (
|
|||
// and the input is a JSON quoted string, Unmarshal calls
|
||||
// [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string.
|
||||
//
|
||||
// To unmarshal JSON into a struct, Unmarshal matches incoming object
|
||||
// keys to 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
|
||||
// default, object keys which don't have a corresponding struct field are
|
||||
// ignored (see [Decoder.DisallowUnknownFields] for an alternative).
|
||||
// To unmarshal JSON into a struct, Unmarshal matches incoming object keys to
|
||||
// the keys used by [Marshal] (either the struct field name or its tag),
|
||||
// ignoring case. If multiple struct fields match an object key, an exact case
|
||||
// match is preferred over a case-insensitive one.
|
||||
//
|
||||
// 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,
|
||||
// Unmarshal stores one of these in the interface value:
|
||||
|
|
|
|||
|
|
@ -4,12 +4,44 @@
|
|||
|
||||
//go:build !goexperiment.jsonv2
|
||||
|
||||
// Package json implements encoding and decoding of JSON as defined in
|
||||
// RFC 7159. The mapping between JSON and Go values is described
|
||||
// in the documentation for the Marshal and Unmarshal functions.
|
||||
// Package json implements encoding and decoding of JSON as defined in RFC 7159.
|
||||
// The mapping between JSON and Go values is described in the documentation for
|
||||
// the Marshal and Unmarshal functions.
|
||||
//
|
||||
// See "JSON and Go" for an introduction to this package:
|
||||
// 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
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ type encodeBuffer struct {
|
|||
|
||||
// maxValue is the approximate maximum Value size passed to WriteValue.
|
||||
maxValue int
|
||||
// unusedCache is the buffer returned by the UnusedBuffer method.
|
||||
unusedCache []byte
|
||||
// availBuffer is the buffer returned by the AvailableBuffer method.
|
||||
availBuffer []byte // always has zero length
|
||||
// bufStats is statistics about buffer utilization.
|
||||
// It is only used with pooled encoders in pools.go.
|
||||
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(`"`)])
|
||||
if !isVerbatim {
|
||||
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)
|
||||
e.unusedCache = b2[:0]
|
||||
e.availBuffer = b2[:0]
|
||||
if err != nil {
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
|
@ -900,20 +900,20 @@ func (e *Encoder) OutputOffset() int64 {
|
|||
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]
|
||||
// being passed to an immediately succeeding [Encoder.WriteValue] call.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// b := d.UnusedBuffer()
|
||||
// b := d.AvailableBuffer()
|
||||
// b = append(b, '"')
|
||||
// b = appendString(b, v) // append the string formatting of v
|
||||
// b = append(b, '"')
|
||||
// ... := d.WriteValue(b)
|
||||
//
|
||||
// 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
|
||||
// need to take special care to avoid mangling the data while reformatting.
|
||||
// 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
|
||||
// the specialized performance optimization for bytes.Buffer.
|
||||
n := 1 << bits.Len(uint(e.s.maxValue|63)) // fast approximation for max length
|
||||
if cap(e.s.unusedCache) < n {
|
||||
e.s.unusedCache = make([]byte, 0, n)
|
||||
if cap(e.s.availBuffer) < 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.
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
|
|||
mk := newAddressableValue(m.Type().Key())
|
||||
mv := newAddressableValue(m.Type().Elem())
|
||||
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 {
|
||||
return newMarshalErrorBefore(enc, m.Type().Key(), err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ func (n Number) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|||
}
|
||||
n = cmp.Or(n, "0")
|
||||
var num []byte
|
||||
val := enc.UnusedBuffer()
|
||||
val := enc.AvailableBuffer()
|
||||
if stringify {
|
||||
val = append(val, '"')
|
||||
val = append(val, n...)
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ type Options = jsonopts.Options
|
|||
// - [jsontext.AllowInvalidUTF8]
|
||||
// - [jsontext.EscapeForHTML]
|
||||
// - [jsontext.EscapeForJS]
|
||||
// - [jsontext.PreserveRawString]
|
||||
// - [jsontext.PreserveRawStrings]
|
||||
//
|
||||
// All other boolean options are set to false.
|
||||
// All non-boolean options are set to the zero value,
|
||||
|
|
|
|||
|
|
@ -57,13 +57,14 @@ type Hash64 interface {
|
|||
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,
|
||||
// unless GOFIPS140=v1.0.0 is set.
|
||||
//
|
||||
// If a hash can only determine at runtime if it can be cloned,
|
||||
// (e.g., if it wraps another hash), it may return [errors.ErrUnsupported].
|
||||
// If a hash can only determine at runtime if it can be cloned (e.g. if it wraps
|
||||
// another hash), it may return an error wrapping [errors.ErrUnsupported].
|
||||
type Cloner interface {
|
||||
Hash
|
||||
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) {
|
||||
var wg sync.WaitGroup
|
||||
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
|
||||
synctest.Run(func() {
|
||||
wg.Add(1)
|
||||
wg.Done()
|
||||
// Count and waiters are 0, so Wait disassociates the WaitGroup.
|
||||
wg.Wait()
|
||||
})
|
||||
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.Done()
|
||||
})
|
||||
}
|
||||
|
||||
func TestWaitGroupMovedBetweenBubblesAfterWait(t *testing.T) {
|
||||
func TestWaitGroupDisassociateInAdd(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
synctest.Run(func() {
|
||||
wg.Go(func() {})
|
||||
wg.Wait()
|
||||
wg.Add(1)
|
||||
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() {
|
||||
// Reusing the WaitGroup is safe, because its count is zero.
|
||||
wg.Go(func() {})
|
||||
wg.Wait()
|
||||
// Reusing the WaitGroup is safe, because it is no longer bubbled.
|
||||
wg.Add(1)
|
||||
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[:])
|
||||
pipeReadDone <- true
|
||||
}()
|
||||
go func() { // func12
|
||||
for {
|
||||
syncPreemptPoint()
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
runtime.GC()
|
||||
|
|
@ -127,3 +132,12 @@ func main() {
|
|||
|
||||
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
|
||||
want := []evDesc{
|
||||
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
|
||||
{"main.main", mainLine + 82},
|
||||
{"runtime.Gosched", 0},
|
||||
{"main.main", mainLine + 87},
|
||||
}},
|
||||
{trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{
|
||||
{"main.main", mainLine + 11},
|
||||
|
|
@ -349,7 +350,7 @@ func TestTraceStacks(t *testing.T) {
|
|||
}},
|
||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||
{"runtime.chansend1", 0},
|
||||
{"main.main", mainLine + 84},
|
||||
{"main.main", mainLine + 89},
|
||||
}},
|
||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||
{"runtime.chansend1", 0},
|
||||
|
|
@ -357,7 +358,7 @@ func TestTraceStacks(t *testing.T) {
|
|||
}},
|
||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||
{"runtime.chanrecv1", 0},
|
||||
{"main.main", mainLine + 85},
|
||||
{"main.main", mainLine + 90},
|
||||
}},
|
||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||
{"runtime.selectgo", 0},
|
||||
|
|
@ -365,7 +366,7 @@ func TestTraceStacks(t *testing.T) {
|
|||
}},
|
||||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||
{"runtime.selectgo", 0},
|
||||
{"main.main", mainLine + 86},
|
||||
{"main.main", mainLine + 91},
|
||||
}},
|
||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||
{"sync.(*Mutex).Lock", 0},
|
||||
|
|
@ -382,7 +383,7 @@ func TestTraceStacks(t *testing.T) {
|
|||
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
|
||||
{"sync.(*WaitGroup).Add", 0},
|
||||
{"sync.(*WaitGroup).Done", 0},
|
||||
{"main.main", mainLine + 91},
|
||||
{"main.main", mainLine + 96},
|
||||
}},
|
||||
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
|
||||
{"sync.(*Cond).Wait", 0},
|
||||
|
|
@ -402,6 +403,10 @@ func TestTraceStacks(t *testing.T) {
|
|||
{"runtime.GOMAXPROCS", 0},
|
||||
{"main.main", 0},
|
||||
}},
|
||||
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
|
||||
{"main.syncPreemptPoint", 0},
|
||||
{"main.main.func12", 0},
|
||||
}},
|
||||
}
|
||||
if !stress {
|
||||
// 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:
|
||||
|
||||
// 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.
|
||||
// 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() {
|
||||
// Abort the Fetch request.
|
||||
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()
|
||||
case resp := <-respCh:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package os
|
|||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
|
|
@ -34,7 +35,15 @@ func removeAll(path string) error {
|
|||
// its parent directory
|
||||
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 parent does not exist, base cannot exist. Fail silently
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -8719,6 +8719,11 @@ func TestTypeAssert(t *testing.T) {
|
|||
testTypeAssert(t, any(int(1)), int(1), true)
|
||||
testTypeAssert(t, any(int(1)), byte(0), false)
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -1514,16 +1514,8 @@ func TypeAssert[T any](v Value) (T, bool) {
|
|||
}
|
||||
|
||||
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:
|
||||
//
|
||||
|
|
@ -1531,17 +1523,23 @@ func TypeAssert[T any](v Value) (T, bool) {
|
|||
// val := ValueOf(&v).Elem()
|
||||
// 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{}
|
||||
// val := ValueOf(&v).Elem()
|
||||
// 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 {
|
||||
v, ok := packIfaceValueIntoEmptyIface(v).(T)
|
||||
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[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
|
||||
|
|
@ -1550,10 +1548,12 @@ func TypeAssert[T any](v Value) (T, bool) {
|
|||
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
|
||||
return zero, false
|
||||
}
|
||||
|
||||
if v.flag&flagIndir == 0 {
|
||||
return *(*T)(unsafe.Pointer(&v.ptr)), true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1059,6 +1059,28 @@ func (mp *m) becomeSpinning() {
|
|||
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 {
|
||||
return mp.ncgo > 0 || mp.isextra
|
||||
}
|
||||
|
|
@ -3307,10 +3329,10 @@ func execute(gp *g, inheritTime bool) {
|
|||
tryRecordGoroutineProfile(gp, nil, osyield)
|
||||
}
|
||||
|
||||
// Assign gp.m before entering _Grunning so running Gs have an
|
||||
// M.
|
||||
// Assign gp.m before entering _Grunning so running Gs have an M.
|
||||
mp.curg = gp
|
||||
gp.m = mp
|
||||
gp.syncSafePoint = false // Clear the flag, which may have been set by morestack.
|
||||
casgstatus(gp, _Grunnable, _Grunning)
|
||||
gp.waitsince = 0
|
||||
gp.preempt = false
|
||||
|
|
@ -3346,6 +3368,11 @@ func findRunnable() (gp *g, inheritTime, tryWakeP bool) {
|
|||
// an M.
|
||||
|
||||
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()
|
||||
if sched.gcwaiting.Load() {
|
||||
gcstopm()
|
||||
|
|
@ -3527,7 +3554,11 @@ top:
|
|||
// which can change underfoot once we no longer block
|
||||
// safe-points. We don't need to snapshot the contents because
|
||||
// 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
|
||||
// len to change out from under us.
|
||||
idlepMaskSnapshot := idlepMask
|
||||
|
|
@ -3668,6 +3699,9 @@ top:
|
|||
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.
|
||||
if netpollinited() && (netpollAnyWaiters() || pollUntil != 0) && sched.lastpoll.Swap(0) != 0 {
|
||||
sched.pollUntil.Store(pollUntil)
|
||||
|
|
@ -4103,6 +4137,11 @@ top:
|
|||
|
||||
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() {
|
||||
// See comment in freezetheworld. We don't want to perturb
|
||||
// scheduler state, so we didn't gcstopm in findRunnable, but
|
||||
|
|
|
|||
|
|
@ -528,11 +528,12 @@ func TestGdbBacktrace(t *testing.T) {
|
|||
got, err := cmd.CombinedOutput()
|
||||
t.Logf("gdb output:\n%s", got)
|
||||
if err != nil {
|
||||
noProcessRE := regexp.MustCompile(`Couldn't get [a-zA-Z_ -]* ?registers: No such process\.`)
|
||||
switch {
|
||||
case bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")):
|
||||
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28551
|
||||
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("reading register pc (#64): No such process.")):
|
||||
// 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
|
||||
lockedm muintptr
|
||||
fipsIndicator uint8
|
||||
syncSafePoint bool // set if g is stopped at a synchronous safe point.
|
||||
runningCleanups atomic.Bool
|
||||
sig uint32
|
||||
writebuf []byte
|
||||
|
|
@ -568,6 +569,7 @@ type m struct {
|
|||
needextram bool
|
||||
g0StackAccurate bool // whether the g0 stack has accurate bounds
|
||||
traceback uint8
|
||||
allpSnapshot []*p // Snapshot of allp for use after dropping P in findRunnable, nil otherwise.
|
||||
ncgocall uint64 // number of cgo calls in total
|
||||
ncgo int32 // number of cgo calls currently in progress
|
||||
cgoCallersUse atomic.Uint32 // if non-zero, cgoCallers in use temporarily
|
||||
|
|
|
|||
|
|
@ -1115,6 +1115,9 @@ func newstack() {
|
|||
shrinkstack(gp)
|
||||
}
|
||||
|
||||
// Set a flag indicated that we've been synchronously preempted.
|
||||
gp.syncSafePoint = true
|
||||
|
||||
if gp.preemptStop {
|
||||
preemptPark(gp) // never returns
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,6 @@ import "C"
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -84,12 +82,8 @@ func GoNeedM() {
|
|||
|
||||
func NeedmDeadlock() {
|
||||
// The failure symptom is that the program hangs because of a
|
||||
// deadlock in needm, so set an alarm.
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
fmt.Println("Hung for 5 seconds")
|
||||
os.Exit(1)
|
||||
}()
|
||||
// deadlock in needm. Instead of using an arbitrary timeout,
|
||||
// we let the test deadline expire if it deadlocks.
|
||||
|
||||
C.runNeedmSignalThread()
|
||||
fmt.Println("OK")
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@ func (tl traceLocker) GoPreempt() {
|
|||
|
||||
// GoStop emits a GoStop event with the provided reason.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -109,10 +109,25 @@ func traceStack(skip int, gp *g, gen uintptr) uint64 {
|
|||
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.syscallbp), pcBuf[2:])
|
||||
} else {
|
||||
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:])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if nstk > 0 {
|
||||
nstk-- // skip runtime.goexit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,13 +120,6 @@ func (wg *WaitGroup) Add(delta int) {
|
|||
if w != 0 && delta > 0 && v == int32(delta) {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
|
@ -140,6 +133,11 @@ func (wg *WaitGroup) Add(delta int) {
|
|||
}
|
||||
// Reset waiters count to 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-- {
|
||||
runtime_Semrelease(&wg.sema, false, 0)
|
||||
}
|
||||
|
|
@ -166,13 +164,20 @@ func (wg *WaitGroup) Wait() {
|
|||
for {
|
||||
state := wg.state.Load()
|
||||
v := int32(state >> 32)
|
||||
w := uint32(state)
|
||||
w := uint32(state & 0x7fffffff)
|
||||
if v == 0 {
|
||||
// Counter is 0, no need to wait.
|
||||
if race.Enabled {
|
||||
race.Enable()
|
||||
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
|
||||
}
|
||||
// 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