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  }