84 lines
1.6 KiB
Go
84 lines
1.6 KiB
Go
|
package system
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"sync"
|
||
|
|
||
|
"emperror.dev/errors"
|
||
|
)
|
||
|
|
||
|
var ErrLockerLocked = errors.Sentinel("locker: cannot acquire lock, already locked")
|
||
|
|
||
|
type Locker struct {
|
||
|
mu sync.RWMutex
|
||
|
ch chan bool
|
||
|
}
|
||
|
|
||
|
// NewLocker returns a new Locker instance.
|
||
|
func NewLocker() *Locker {
|
||
|
return &Locker{
|
||
|
ch: make(chan bool, 1),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// IsLocked returns the current state of the locker channel. If there is
|
||
|
// currently a value in the channel, it is assumed to be locked.
|
||
|
func (l *Locker) IsLocked() bool {
|
||
|
l.mu.RLock()
|
||
|
defer l.mu.RUnlock()
|
||
|
return len(l.ch) == 1
|
||
|
}
|
||
|
|
||
|
// Acquire will acquire the power lock if it is not currently locked. If it is
|
||
|
// already locked, acquire will fail to acquire the lock, and will return false.
|
||
|
func (l *Locker) Acquire() error {
|
||
|
l.mu.Lock()
|
||
|
defer l.mu.Unlock()
|
||
|
select {
|
||
|
case l.ch <- true:
|
||
|
default:
|
||
|
return ErrLockerLocked
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
|
||
|
// TryAcquire will attempt to acquire a power-lock until the context provided
|
||
|
// is canceled.
|
||
|
func (l *Locker) TryAcquire(ctx context.Context) error {
|
||
|
select {
|
||
|
case l.ch <- true:
|
||
|
return nil
|
||
|
case <-ctx.Done():
|
||
|
if err := ctx.Err(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Release will drain the locker channel so that we can properly re-acquire it
|
||
|
// at a later time. If the channel is not currently locked this function is a
|
||
|
// no-op and will immediately return.
|
||
|
func (l *Locker) Release() {
|
||
|
l.mu.Lock()
|
||
|
select {
|
||
|
case <-l.ch:
|
||
|
default:
|
||
|
}
|
||
|
l.mu.Unlock()
|
||
|
}
|
||
|
|
||
|
// Destroy cleans up the power locker by closing the channel.
|
||
|
func (l *Locker) Destroy() {
|
||
|
l.mu.Lock()
|
||
|
if l.ch != nil {
|
||
|
select {
|
||
|
case <-l.ch:
|
||
|
default:
|
||
|
}
|
||
|
close(l.ch)
|
||
|
}
|
||
|
l.mu.Unlock()
|
||
|
}
|