codeberg.org/gruf/go-mutexes@v1.5.0/mutex_timeout.go (about)

     1  package mutexes
     2  
     3  import (
     4  	"time"
     5  )
     6  
     7  // TimeoutMutex defines a Mutex with timeouts on locks
     8  type TimeoutMutex interface {
     9  	Mutex
    10  
    11  	// LockFunc is functionally the same as Lock(), but allows setting a custom hook called on timeout
    12  	LockFunc(func()) func()
    13  }
    14  
    15  // TimeoutRWMutex defines a RWMutex with timeouts on locks
    16  type TimeoutRWMutex interface {
    17  	RWMutex
    18  
    19  	// LockFunc is functionally the same as Lock(), but allows setting a custom hook called on timeout
    20  	LockFunc(func()) func()
    21  
    22  	// RLockFunc is functionally the same as RLock(), but allows setting a custom hook called on timeout
    23  	RLockFunc(func()) func()
    24  }
    25  
    26  // WithTimeout wraps the supplied Mutex to add a timeout
    27  func WithTimeout(mu Mutex, d time.Duration) TimeoutMutex {
    28  	return &timeoutMutex{mu: mu, d: d}
    29  }
    30  
    31  // WithTimeoutRW wraps the supplied RWMutex to add read/write timeouts
    32  func WithTimeoutRW(mu RWMutex, rd, wd time.Duration) TimeoutRWMutex {
    33  	return &timeoutRWMutex{mu: mu, rd: rd, wd: wd}
    34  }
    35  
    36  // timeoutMutex wraps a Mutex with timeout
    37  type timeoutMutex struct {
    38  	mu Mutex         // mu is the wrapped mutex
    39  	d  time.Duration // d is the timeout duration
    40  }
    41  
    42  func (mu *timeoutMutex) Lock() func() {
    43  	return mu.LockFunc(func() { panic("lock timed out") })
    44  }
    45  
    46  func (mu *timeoutMutex) LockFunc(fn func()) func() {
    47  	return mutexTimeout(mu.d, mu.mu.Lock(), fn)
    48  }
    49  
    50  // TimeoutRWMutex wraps a RWMutex with timeouts
    51  type timeoutRWMutex struct {
    52  	mu RWMutex       // mu is the wrapped rwmutex
    53  	rd time.Duration // rd is the rlock timeout duration
    54  	wd time.Duration // wd is the lock timeout duration
    55  }
    56  
    57  func (mu *timeoutRWMutex) Lock() func() {
    58  	return mu.LockFunc(func() { panic("lock timed out") })
    59  }
    60  
    61  func (mu *timeoutRWMutex) LockFunc(fn func()) func() {
    62  	return mutexTimeout(mu.wd, mu.mu.Lock(), fn)
    63  }
    64  
    65  func (mu *timeoutRWMutex) RLock() func() {
    66  	return mu.RLockFunc(func() { panic("rlock timed out") })
    67  }
    68  
    69  func (mu *timeoutRWMutex) RLockFunc(fn func()) func() {
    70  	return mutexTimeout(mu.rd, mu.mu.RLock(), fn)
    71  }
    72  
    73  // mutexTimeout performs a timed unlock, calling supplied fn if timeout is reached
    74  func mutexTimeout(d time.Duration, unlock func(), fn func()) func() {
    75  	if d < 1 {
    76  		// No timeout, just unlock
    77  		return unlock
    78  	}
    79  
    80  	// Start timer to call fn.
    81  	t := time.AfterFunc(d, fn)
    82  
    83  	// Wrap unlock to stop mutex timer.
    84  	return func() { t.Stop(); unlock() }
    85  }