80 строки
1,4 КиБ
Go
80 строки
1,4 КиБ
Go
package sync
|
|
|
|
import "internal/task"
|
|
|
|
type Cond struct {
|
|
L Locker
|
|
|
|
unlocking *earlySignal
|
|
blocked task.Stack
|
|
}
|
|
|
|
// earlySignal is a type used to implement a stack for signalling waiters while they are unlocking.
|
|
type earlySignal struct {
|
|
next *earlySignal
|
|
|
|
signaled bool
|
|
}
|
|
|
|
func (c *Cond) trySignal() bool {
|
|
// Pop a blocked task off of the stack, and schedule it if applicable.
|
|
t := c.blocked.Pop()
|
|
if t != nil {
|
|
scheduleTask(t)
|
|
return true
|
|
}
|
|
|
|
// If there any tasks which are currently unlocking, signal one.
|
|
if c.unlocking != nil {
|
|
c.unlocking.signaled = true
|
|
c.unlocking = c.unlocking.next
|
|
return true
|
|
}
|
|
|
|
// There was nothing to signal.
|
|
return false
|
|
}
|
|
|
|
func (c *Cond) Signal() {
|
|
c.trySignal()
|
|
}
|
|
|
|
func (c *Cond) Broadcast() {
|
|
// Signal everything.
|
|
for c.trySignal() {
|
|
}
|
|
}
|
|
|
|
func (c *Cond) Wait() {
|
|
// Add an earlySignal frame to the stack so we can be signalled while unlocking.
|
|
early := earlySignal{
|
|
next: c.unlocking,
|
|
}
|
|
c.unlocking = &early
|
|
|
|
// Temporarily unlock L.
|
|
c.L.Unlock()
|
|
|
|
// Re-acquire the lock before returning.
|
|
defer c.L.Lock()
|
|
|
|
// If we were signaled while unlocking, immediately complete.
|
|
if early.signaled {
|
|
return
|
|
}
|
|
|
|
// Remove the earlySignal frame.
|
|
prev := c.unlocking
|
|
for prev != nil && prev.next != &early {
|
|
prev = prev.next
|
|
}
|
|
if prev != nil {
|
|
prev.next = early.next
|
|
} else {
|
|
c.unlocking = early.next
|
|
}
|
|
|
|
// Wait for a signal.
|
|
c.blocked.Push(task.Current())
|
|
task.Pause()
|
|
}
|