diff options
| author | Damien Neil <dneil@google.com> | 2025-06-05 13:47:06 -0700 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-06-10 15:02:26 -0700 |
| commit | 4f86f2267167a63b673c4a2a2994e008b32c90ea (patch) | |
| tree | 24dd8afd3e0e54897f199a95a151ccd5be83fab7 /src/internal | |
| parent | 773701a853a3105696c59c2b92b2eff35e0e055b (diff) | |
| download | go-4f86f2267167a63b673c4a2a2994e008b32c90ea.tar.xz | |
testing/synctest, runtime: avoid panic when using linker-alloc WG from bubble
We associate WaitGroups with synctest bubbles by attaching a
special to the WaitGroup. It is not possible to attach a special
to a linker-allocated value, such as:
var wg sync.WaitGroup
Avoid panicking when accessing a linker-allocated WaitGroup
from a bubble. We have no way to associate these WaitGroups
with a bubble, so just treat them as always unbubbled.
This is probably fine, since the WaitGroup was always
created outside the bubble in this case.
Fixes #74005
Change-Id: Ic71514b0b8d0cecd62e45cc929ffcbeb16f54a55
Reviewed-on: https://go-review.googlesource.com/c/go/+/679695
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/internal')
| -rw-r--r-- | src/internal/synctest/synctest.go | 22 | ||||
| -rw-r--r-- | src/internal/synctest/synctest_test.go | 28 |
2 files changed, 45 insertions, 5 deletions
diff --git a/src/internal/synctest/synctest.go b/src/internal/synctest/synctest.go index 4d7fa3730c..cb3333a627 100644 --- a/src/internal/synctest/synctest.go +++ b/src/internal/synctest/synctest.go @@ -8,6 +8,7 @@ package synctest import ( + "internal/abi" "unsafe" ) @@ -22,14 +23,25 @@ func Wait() //go:linkname IsInBubble func IsInBubble() bool -// Associate associates p with the current bubble. -// It returns false if p has an existing association with a different bubble. -func Associate[T any](p *T) (ok bool) { - return associate(unsafe.Pointer(p)) +// Association is the state of a pointer's bubble association. +type Association int + +const ( + Unbubbled = Association(iota) // not associated with any bubble + CurrentBubble // associated with the current bubble + OtherBubble // associated with a different bubble +) + +// Associate attempts to associate p with the current bubble. +// It returns the new association status of p. +func Associate[T any](p *T) Association { + // Ensure p escapes to permit us to attach a special to it. + escapedP := abi.Escape(p) + return Association(associate(unsafe.Pointer(escapedP))) } //go:linkname associate -func associate(p unsafe.Pointer) bool +func associate(p unsafe.Pointer) int // Disassociate disassociates p from any bubble. func Disassociate[T any](p *T) { diff --git a/src/internal/synctest/synctest_test.go b/src/internal/synctest/synctest_test.go index fe6eb63702..222cae2597 100644 --- a/src/internal/synctest/synctest_test.go +++ b/src/internal/synctest/synctest_test.go @@ -731,6 +731,34 @@ func TestWaitGroupMovedBetweenBubblesAfterWait(t *testing.T) { }) } +var testWaitGroupLinkerAllocatedWG sync.WaitGroup + +func TestWaitGroupLinkerAllocated(t *testing.T) { + synctest.Run(func() { + // This WaitGroup is probably linker-allocated and has no span, + // so we won't be able to add a special to it associating it with + // this bubble. + // + // Operations on it may not be durably blocking, + // but they shouldn't fail. + testWaitGroupLinkerAllocatedWG.Go(func() {}) + testWaitGroupLinkerAllocatedWG.Wait() + }) +} + +var testWaitGroupHeapAllocatedWG = new(sync.WaitGroup) + +func TestWaitGroupHeapAllocated(t *testing.T) { + synctest.Run(func() { + // This package-scoped WaitGroup var should have been heap-allocated, + // so we can associate it with a bubble. + testWaitGroupHeapAllocatedWG.Add(1) + go testWaitGroupHeapAllocatedWG.Wait() + synctest.Wait() + testWaitGroupHeapAllocatedWG.Done() + }) +} + func TestHappensBefore(t *testing.T) { // Use two parallel goroutines accessing different vars to ensure that // we correctly account for multiple goroutines in the bubble. |
