github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/cond.go (about) 1 //go:build !scheduler.none 2 3 package runtime 4 5 import ( 6 "internal/task" 7 "sync/atomic" 8 "unsafe" 9 ) 10 11 // notifiedPlaceholder is a placeholder task which is used to indicate that the condition variable has been notified. 12 var notifiedPlaceholder task.Task 13 14 // Cond is a simplified condition variable, useful for notifying goroutines of interrupts. 15 type Cond struct { 16 t *task.Task 17 } 18 19 // Notify sends a notification. 20 // If the condition variable already has a pending notification, this returns false. 21 func (c *Cond) Notify() bool { 22 for { 23 t := (*task.Task)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)))) 24 switch t { 25 case nil: 26 // Nothing is waiting yet. 27 // Apply the notification placeholder. 28 if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)), unsafe.Pointer(t), unsafe.Pointer(¬ifiedPlaceholder)) { 29 return true 30 } 31 case ¬ifiedPlaceholder: 32 // The condition variable has already been notified. 33 return false 34 default: 35 // Unblock the waiting task. 36 if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)), unsafe.Pointer(t), nil) { 37 runqueuePushBack(t) 38 return true 39 } 40 } 41 } 42 } 43 44 // Poll checks for a notification. 45 // If a notification is found, it is cleared and this returns true. 46 func (c *Cond) Poll() bool { 47 for { 48 t := (*task.Task)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)))) 49 switch t { 50 case nil: 51 // No notifications are present. 52 return false 53 case ¬ifiedPlaceholder: 54 // A notification arrived and there is no waiting goroutine. 55 // Clear the notification and return. 56 if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)), unsafe.Pointer(t), nil) { 57 return true 58 } 59 default: 60 // A task is blocked on the condition variable, which means it has not been notified. 61 return false 62 } 63 } 64 } 65 66 // Wait for a notification. 67 // If the condition variable was previously notified, this returns immediately. 68 func (c *Cond) Wait() { 69 cur := task.Current() 70 for { 71 t := (*task.Task)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)))) 72 switch t { 73 case nil: 74 // Condition variable has not been notified. 75 // Block the current task on the condition variable. 76 if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)), nil, unsafe.Pointer(cur)) { 77 task.Pause() 78 return 79 } 80 case ¬ifiedPlaceholder: 81 // A notification arrived and there is no waiting goroutine. 82 // Clear the notification and return. 83 if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.t)), unsafe.Pointer(t), nil) { 84 return 85 } 86 default: 87 panic("interrupt.Cond: condition variable in use by another goroutine") 88 } 89 } 90 }