1 package sync
2 3 import (
4 "internal/task"
5 "unsafe"
6 )
7 8 // Condition variable.
9 // A goroutine that called Wait() can be in one of a few states depending on the
10 // Task.Data field:
11 // - When entering Wait, and before going to sleep, the data field is 0.
12 // - When the goroutine that calls Wait changes its data value from 0 to 1, it
13 // is going to sleep. It has not been awoken early.
14 // - When instead a call to Signal or Broadcast can change the data field from 0
15 // to 1, it will _not_ go to sleep but be signalled early.
16 // This can happen when a concurrent call to Signal happens, or the Unlock
17 // function calls Signal for some reason.
18 19 type Cond struct {
20 L Locker
21 22 blocked task.Stack
23 lock task.PMutex
24 }
25 26 func NewCond(l Locker) *Cond {
27 return &Cond{L: l}
28 }
29 30 func (c *Cond) trySignal() bool {
31 // Pop a blocked task off of the stack, and schedule it if applicable.
32 t := c.blocked.Pop()
33 if t != nil {
34 dataPtr := (*task.Uint32)(unsafe.Pointer(&t.Data))
35 36 // The data value is 0 when the task is not yet sleeping, and 1 when it is.
37 if dataPtr.Swap(1) != 0 {
38 // The value was already 1, so the task went to sleep (or is about to go
39 // to sleep). Schedule the task to be resumed.
40 scheduleTask(t)
41 }
42 return true
43 }
44 45 // There was nothing to signal.
46 return false
47 }
48 49 func (c *Cond) Signal() {
50 c.lock.Lock()
51 c.trySignal()
52 c.lock.Unlock()
53 }
54 55 func (c *Cond) Broadcast() {
56 // Signal everything.
57 c.lock.Lock()
58 for c.trySignal() {
59 }
60 c.lock.Unlock()
61 }
62 63 func (c *Cond) Wait() {
64 // Mark us as not yet signalled or sleeping.
65 t := task.Current()
66 dataPtr := (*task.Uint32)(unsafe.Pointer(&t.Data))
67 dataPtr.Store(0)
68 69 // Add us to the list of waiting goroutines.
70 c.lock.Lock()
71 c.blocked.Push(t)
72 c.lock.Unlock()
73 74 // Temporarily unlock L.
75 c.L.Unlock()
76 77 // Re-acquire the lock before returning.
78 defer c.L.Lock()
79 80 // If we were signaled while unlocking, immediately complete.
81 if dataPtr.Swap(1) != 0 {
82 // The data value was already 1, so we got a signal already (and weren't
83 // scheduled because trySignal was the first to change the value).
84 return
85 }
86 87 // We were the first to change the value from 0 to 1, meaning we did not get
88 // a signal during the call to Unlock(). So we wait until we do get a
89 // signal.
90 task.Pause()
91 }
92 93 //go:linkname scheduleTask runtime.scheduleTask
94 func scheduleTask(*task.Task)
95