mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
* improv. timed mutex * Add all protection back in and jankyness because races. :'( * Add intial benchmarkeroos * Add master benchmarks * goodness me * what? * what again? * glorious: nits * just a swaperino instead --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package timedmutex
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// TimedMutex is a blocking mutex which will unlock after a specified time
|
|
type TimedMutex struct {
|
|
// primary mutex is the main lock that will be unlocked after the duration
|
|
primary sync.Mutex
|
|
// secondary mutex is used to protect the timer
|
|
secondary sync.Mutex
|
|
timer *time.Timer
|
|
// primed is used to determine if the timer has been started this is
|
|
// slightly more performant than checking the timer directly and interacting
|
|
// with a RW mutex.
|
|
primed atomic.Bool
|
|
duration time.Duration
|
|
}
|
|
|
|
// NewTimedMutex creates a new timed mutex with a specified duration
|
|
func NewTimedMutex(length time.Duration) *TimedMutex {
|
|
return &TimedMutex{duration: length}
|
|
}
|
|
|
|
// LockForDuration will start a timer, lock the mutex, then allow the caller to continue
|
|
// After the duration, the mutex will be unlocked
|
|
func (t *TimedMutex) LockForDuration() {
|
|
t.primary.Lock()
|
|
if !t.primed.Swap(true) {
|
|
t.secondary.Lock()
|
|
t.timer = time.AfterFunc(t.duration, func() { t.primary.Unlock() })
|
|
t.secondary.Unlock()
|
|
} else {
|
|
// Timer C channel is not used with AfterFunc, so no need to drain.
|
|
t.secondary.Lock()
|
|
t.timer.Reset(t.duration)
|
|
t.secondary.Unlock()
|
|
}
|
|
}
|
|
|
|
// UnlockIfLocked will unlock the mutex if its currently locked Will return true
|
|
// if successfully unlocked
|
|
func (t *TimedMutex) UnlockIfLocked() bool {
|
|
if !t.primed.Load() {
|
|
return false
|
|
}
|
|
|
|
t.secondary.Lock()
|
|
wasStoppedByCall := t.timer.Stop()
|
|
t.secondary.Unlock()
|
|
|
|
if !wasStoppedByCall {
|
|
// Timer has already fired and the mutex has been unlocked.
|
|
// Timer C channel is not used with AfterFunc, so no need to drain.
|
|
return false
|
|
}
|
|
t.primary.Unlock()
|
|
return true
|
|
}
|