1 // Copyright 2024 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 // Package synctest provides support for testing concurrent code.
6 //
7 // See the testing/synctest package for function documentation.
8 package synctest
9 10 import (
11 "internal/abi"
12 "unsafe"
13 )
14 15 //go:linkname Run
16 func Run(f func())
17 18 //go:linkname Wait
19 func Wait()
20 21 // IsInBubble reports whether the current goroutine is in a bubble.
22 //
23 //go:linkname IsInBubble
24 func IsInBubble() bool
25 26 // Association is the state of a pointer's bubble association.
27 type Association int
28 29 const (
30 Unbubbled = Association(iota) // not associated with any bubble
31 CurrentBubble // associated with the current bubble
32 OtherBubble // associated with a different bubble
33 )
34 35 // Associate attempts to associate p with the current bubble.
36 // It returns the new association status of p.
37 func Associate[T any](p *T) Association {
38 // Ensure p escapes to permit us to attach a special to it.
39 escapedP := abi.Escape(p)
40 return Association(associate(unsafe.Pointer(escapedP)))
41 }
42 43 //go:linkname associate
44 func associate(p unsafe.Pointer) int
45 46 // Disassociate disassociates p from any bubble.
47 func Disassociate[T any](p *T) {
48 disassociate(unsafe.Pointer(p))
49 }
50 51 //go:linkname disassociate
52 func disassociate(b unsafe.Pointer)
53 54 // IsAssociated reports whether p is associated with the current bubble.
55 func IsAssociated[T any](p *T) bool {
56 return isAssociated(unsafe.Pointer(p))
57 }
58 59 //go:linkname isAssociated
60 func isAssociated(p unsafe.Pointer) bool
61 62 //go:linkname acquire
63 func acquire() any
64 65 //go:linkname release
66 func release(any)
67 68 //go:linkname inBubble
69 func inBubble(any, func())
70 71 // A Bubble is a synctest bubble.
72 //
73 // Not a public API. Used by syscall/js to propagate bubble membership through syscalls.
74 type Bubble struct {
75 b any
76 }
77 78 // Acquire returns a reference to the current goroutine's bubble.
79 // The bubble will not become idle until Release is called.
80 func Acquire() *Bubble {
81 if b := acquire(); b != nil {
82 return &Bubble{b}
83 }
84 return nil
85 }
86 87 // Release releases the reference to the bubble,
88 // allowing it to become idle again.
89 func (b *Bubble) Release() {
90 if b == nil {
91 return
92 }
93 release(b.b)
94 b.b = nil
95 }
96 97 // Run executes f in the bubble.
98 // The current goroutine must not be part of a bubble.
99 func (b *Bubble) Run(f func()) {
100 if b == nil {
101 f()
102 } else {
103 inBubble(b.b, f)
104 }
105 }
106