// Copyright 2024 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 synctest provides support for testing concurrent code. // // See the testing/synctest package for function documentation. package synctest import ( "internal/abi" "unsafe" ) //go:linkname Run func Run(f func()) //go:linkname Wait func Wait() // IsInBubble reports whether the current goroutine is in a bubble. // //go:linkname IsInBubble func IsInBubble() bool // 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) int // Disassociate disassociates p from any bubble. func Disassociate[T any](p *T) { disassociate(unsafe.Pointer(p)) } //go:linkname disassociate func disassociate(b unsafe.Pointer) // IsAssociated reports whether p is associated with the current bubble. func IsAssociated[T any](p *T) bool { return isAssociated(unsafe.Pointer(p)) } //go:linkname isAssociated func isAssociated(p unsafe.Pointer) bool //go:linkname acquire func acquire() any //go:linkname release func release(any) //go:linkname inBubble func inBubble(any, func()) // A Bubble is a synctest bubble. // // Not a public API. Used by syscall/js to propagate bubble membership through syscalls. type Bubble struct { b any } // Acquire returns a reference to the current goroutine's bubble. // The bubble will not become idle until Release is called. func Acquire() *Bubble { if b := acquire(); b != nil { return &Bubble{b} } return nil } // Release releases the reference to the bubble, // allowing it to become idle again. func (b *Bubble) Release() { if b == nil { return } release(b.b) b.b = nil } // Run executes f in the bubble. // The current goroutine must not be part of a bubble. func (b *Bubble) Run(f func()) { if b == nil { f() } else { inBubble(b.b, f) } }