github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/sync/cond.go (about) 1 package sync 2 3 import "internal/task" 4 5 type Cond struct { 6 L Locker 7 8 unlocking *earlySignal 9 blocked task.Stack 10 } 11 12 // earlySignal is a type used to implement a stack for signalling waiters while they are unlocking. 13 type earlySignal struct { 14 next *earlySignal 15 16 signaled bool 17 } 18 19 func NewCond(l Locker) *Cond { 20 return &Cond{L: l} 21 } 22 23 func (c *Cond) trySignal() bool { 24 // Pop a blocked task off of the stack, and schedule it if applicable. 25 t := c.blocked.Pop() 26 if t != nil { 27 scheduleTask(t) 28 return true 29 } 30 31 // If there any tasks which are currently unlocking, signal one. 32 if c.unlocking != nil { 33 c.unlocking.signaled = true 34 c.unlocking = c.unlocking.next 35 return true 36 } 37 38 // There was nothing to signal. 39 return false 40 } 41 42 func (c *Cond) Signal() { 43 c.trySignal() 44 } 45 46 func (c *Cond) Broadcast() { 47 // Signal everything. 48 for c.trySignal() { 49 } 50 } 51 52 func (c *Cond) Wait() { 53 // Add an earlySignal frame to the stack so we can be signalled while unlocking. 54 early := earlySignal{ 55 next: c.unlocking, 56 } 57 c.unlocking = &early 58 59 // Temporarily unlock L. 60 c.L.Unlock() 61 62 // Re-acquire the lock before returning. 63 defer c.L.Lock() 64 65 // If we were signaled while unlocking, immediately complete. 66 if early.signaled { 67 return 68 } 69 70 // Remove the earlySignal frame. 71 prev := c.unlocking 72 for prev != nil && prev.next != &early { 73 prev = prev.next 74 } 75 if prev != nil { 76 prev.next = early.next 77 } else { 78 c.unlocking = early.next 79 } 80 81 // Wait for a signal. 82 c.blocked.Push(task.Current()) 83 task.Pause() 84 }