summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-mutexes/mutex_timeout.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/codeberg.org/gruf/go-mutexes/mutex_timeout.go')
-rw-r--r--vendor/codeberg.org/gruf/go-mutexes/mutex_timeout.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-mutexes/mutex_timeout.go b/vendor/codeberg.org/gruf/go-mutexes/mutex_timeout.go
new file mode 100644
index 000000000..5da69ef25
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-mutexes/mutex_timeout.go
@@ -0,0 +1,105 @@
+package mutexes
+
+import (
+ "sync"
+ "time"
+
+ "codeberg.org/gruf/go-nowish"
+)
+
+// TimeoutMutex defines a Mutex with timeouts on locks
+type TimeoutMutex interface {
+ Mutex
+
+ // LockFunc is functionally the same as Lock(), but allows setting a custom hook called on timeout
+ LockFunc(func()) func()
+}
+
+// TimeoutRWMutex defines a RWMutex with timeouts on locks
+type TimeoutRWMutex interface {
+ RWMutex
+
+ // LockFunc is functionally the same as Lock(), but allows setting a custom hook called on timeout
+ LockFunc(func()) func()
+
+ // RLockFunc is functionally the same as RLock(), but allows setting a custom hook called on timeout
+ RLockFunc(func()) func()
+}
+
+// WithTimeout wraps the supplied Mutex to add a timeout
+func WithTimeout(mu Mutex, d time.Duration) TimeoutMutex {
+ return &timeoutMutex{mu: mu, d: d}
+}
+
+// WithTimeoutRW wraps the supplied RWMutex to add read/write timeouts
+func WithTimeoutRW(mu RWMutex, rd, wd time.Duration) TimeoutRWMutex {
+ return &timeoutRWMutex{mu: mu, rd: rd, wd: wd}
+}
+
+// timeoutMutex wraps a Mutex with timeout
+type timeoutMutex struct {
+ mu Mutex // mu is the wrapped mutex
+ d time.Duration // d is the timeout duration
+}
+
+func (mu *timeoutMutex) Lock() func() {
+ return mu.LockFunc(func() { panic("lock timed out") })
+}
+
+func (mu *timeoutMutex) LockFunc(fn func()) func() {
+ return mutexTimeout(mu.d, mu.mu.Lock(), fn)
+}
+
+// TimeoutRWMutex wraps a RWMutex with timeouts
+type timeoutRWMutex struct {
+ mu RWMutex // mu is the wrapped rwmutex
+ rd time.Duration // rd is the rlock timeout duration
+ wd time.Duration // wd is the lock timeout duration
+}
+
+func (mu *timeoutRWMutex) Lock() func() {
+ return mu.LockFunc(func() { panic("lock timed out") })
+}
+
+func (mu *timeoutRWMutex) LockFunc(fn func()) func() {
+ return mutexTimeout(mu.wd, mu.mu.Lock(), fn)
+}
+
+func (mu *timeoutRWMutex) RLock() func() {
+ return mu.RLockFunc(func() { panic("rlock timed out") })
+}
+
+func (mu *timeoutRWMutex) RLockFunc(fn func()) func() {
+ return mutexTimeout(mu.rd, mu.mu.RLock(), fn)
+}
+
+// timeoutPool provides nowish.Timeout objects for timeout mutexes
+var timeoutPool = sync.Pool{
+ New: func() interface{} {
+ t := nowish.NewTimeout()
+ return &t
+ },
+}
+
+// mutexTimeout performs a timed unlock, calling supplied fn if timeout is reached
+func mutexTimeout(d time.Duration, unlock func(), fn func()) func() {
+ if d < 1 {
+ // No timeout, just unlock
+ return unlock
+ }
+
+ // Acquire timeout obj
+ t := timeoutPool.Get().(*nowish.Timeout)
+
+ // Start the timeout with hook
+ t.Start(d, fn)
+
+ // Return func cancelling timeout,
+ // replacing Timeout in pool and
+ // finally unlocking mutex
+ return func() {
+ t.Cancel()
+ timeoutPool.Put(t)
+ unlock()
+ }
+}