synctest.mx raw

   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