codeberg.org/gruf/go-mutexes@v1.5.0/mutex_safe.go (about) 1 package mutexes 2 3 import ( 4 "sync/atomic" 5 ) 6 7 // WithSafety wrapps the supplied Mutex to protect unlock fns 8 // from being called multiple times 9 func WithSafety(mu Mutex) Mutex { 10 return &safeMutex{mu: mu} 11 } 12 13 // WithSafetyRW wrapps the supplied RWMutex to protect unlock 14 // fns from being called multiple times 15 func WithSafetyRW(mu RWMutex) RWMutex { 16 return &safeRWMutex{mu: mu} 17 } 18 19 // safeMutex simply wraps a Mutex to add multi-unlock safety 20 type safeMutex struct{ mu Mutex } 21 22 func (mu *safeMutex) Lock() func() { 23 unlock := mu.mu.Lock() 24 return once(unlock) 25 } 26 27 // safeRWMutex simply wraps a RWMutex to add multi-unlock safety 28 type safeRWMutex struct{ mu RWMutex } 29 30 func (mu *safeRWMutex) Lock() func() { 31 unlock := mu.mu.Lock() 32 return once(unlock) 33 } 34 35 func (mu *safeRWMutex) RLock() func() { 36 unlock := mu.mu.RLock() 37 return once(unlock) 38 } 39 40 // once will perform 'do' only once, this is safe for unlocks 41 // as 2 functions calling 'unlock()' don't need absolute guarantees 42 // that by the time it is completed the unlock was finished. 43 func once(do func()) func() { 44 var done uint32 45 return func() { 46 if atomic.CompareAndSwapUint32(&done, 0, 1) { 47 do() 48 } 49 } 50 }