82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
// Package trigger implements channel-based condition variable.
|
|
package trigger
|
|
|
|
import "sync"
|
|
|
|
// A Cond is a condition shared by multiple goroutines. The [Cond.Ready]
|
|
// method returns a channel that is closed when the condition is activated.
|
|
//
|
|
// When a condition is first created it is inactive. While inactive, reads on
|
|
// the ready channel will block. The condition remains inactive until
|
|
// [Cond.Set] or [Cond.Signal] is called, either of which causes the current
|
|
// ready channel to be closed (and thus deliver a zero value). Once a condition
|
|
// has been activated, it remains active until it is reset. Use [Cond.Reset] to
|
|
// make it inactive again.
|
|
//
|
|
// The [Cond.Signal] method activates and then immediately resets the
|
|
// condition, acting as Set and Reset done in a single step.
|
|
//
|
|
// A zero Cond is ready for use, and is inactive, but must not be copied after
|
|
// any of its methods have been called.
|
|
type Cond struct {
|
|
μ sync.Mutex
|
|
ch chan struct{}
|
|
closed bool
|
|
|
|
// The signal channel is lazily initialized by the first waiter.
|
|
}
|
|
|
|
// New constructs a new inactive [Cond].
|
|
func New() *Cond { return new(Cond) }
|
|
|
|
// Signal activates and immediately resets the condition. If the condition was
|
|
// already active, this is equivalent to [Cond.Reset].
|
|
func (c *Cond) Signal() {
|
|
c.μ.Lock()
|
|
defer c.μ.Unlock()
|
|
|
|
if c.ch != nil && !c.closed {
|
|
close(c.ch) // wake any pending waiters
|
|
}
|
|
c.ch = nil
|
|
c.closed = false
|
|
}
|
|
|
|
// Set activates the condition. If it was already active, Set has no effect.
|
|
func (c *Cond) Set() {
|
|
c.μ.Lock()
|
|
defer c.μ.Unlock()
|
|
|
|
if c.ch == nil {
|
|
c.ch = make(chan struct{})
|
|
}
|
|
if !c.closed {
|
|
close(c.ch)
|
|
c.closed = true
|
|
}
|
|
}
|
|
|
|
// Reset resets the condition. If it was already inactive, Reset has no effect.
|
|
func (c *Cond) Reset() {
|
|
c.μ.Lock()
|
|
defer c.μ.Unlock()
|
|
|
|
if c.closed {
|
|
c.ch = nil
|
|
c.closed = false
|
|
}
|
|
}
|
|
|
|
// Ready returns a channel that is closed when c is activated. If c is active
|
|
// when Ready is called, the returned channel will already be closed.
|
|
func (c *Cond) Ready() <-chan struct{} {
|
|
c.μ.Lock()
|
|
defer c.μ.Unlock()
|
|
|
|
if c.ch == nil {
|
|
c.ch = make(chan struct{})
|
|
c.closed = false
|
|
}
|
|
return c.ch
|
|
}
|