github.com/go-playground/pkg/v5@v5.29.1/sync/mutex2.go (about) 1 //go:build go1.18 2 // +build go1.18 3 4 package syncext 5 6 import ( 7 "sync" 8 9 resultext "github.com/go-playground/pkg/v5/values/result" 10 ) 11 12 // MutexGuard protects the inner contents of a Mutex2 for safety and unlocking. 13 type MutexGuard[T any, M interface{ Unlock() }] struct { 14 m M 15 // T is the inner generic type of the Mutex 16 T T 17 } 18 19 // Unlock unlocks the Mutex2 value. 20 func (g MutexGuard[T, M]) Unlock() { 21 g.m.Unlock() 22 } 23 24 // NewMutex2 creates a new Mutex for use. 25 func NewMutex2[T any](value T) Mutex2[T] { 26 return Mutex2[T]{ 27 m: new(sync.Mutex), 28 value: value, 29 } 30 } 31 32 // Mutex2 creates a type safe mutex wrapper ensuring one cannot access the values of a locked values 33 // without first gaining a lock. 34 type Mutex2[T any] struct { 35 m *sync.Mutex 36 value T 37 } 38 39 // Lock locks the Mutex and returns value for use, safe for mutation if 40 // 41 // If the lock is already in use, the calling goroutine blocks until the mutex is available. 42 func (m Mutex2[T]) Lock() MutexGuard[T, *sync.Mutex] { 43 m.m.Lock() 44 return MutexGuard[T, *sync.Mutex]{ 45 m: m.m, 46 T: m.value, 47 } 48 } 49 50 //// Unlock unlocks the Mutex accepting a value to set as the new or mutated value. 51 //// It is optional to pass a new value to be set but NOT required for there reasons: 52 //// 1. If the internal value is already mutable then no need to set as changes apply as they happen. 53 //// 2. If there's a failure working with the locked value you may NOT want to set it, but still unlock. 54 //// 3. Supports locked values that are not mutable. 55 //// 56 //// It is a run-time error if the Mutex is not locked on entry to Unlock. 57 //func (m Mutex2[T]) Unlock() { 58 // m.m.Unlock() 59 //} 60 61 // PerformMut safely locks and unlocks the Mutex values and performs the provided function returning its error if one 62 // otherwise setting the returned value as the new mutex value. 63 func (m Mutex2[T]) PerformMut(f func(T)) { 64 guard := m.Lock() 65 f(guard.T) 66 guard.Unlock() 67 } 68 69 // TryLock tries to lock Mutex and reports whether it succeeded. 70 // If it does the value is returned for use in the Ok result otherwise Err with empty value. 71 func (m Mutex2[T]) TryLock() resultext.Result[MutexGuard[T, *sync.Mutex], struct{}] { 72 if m.m.TryLock() { 73 return resultext.Ok[MutexGuard[T, *sync.Mutex], struct{}](MutexGuard[T, *sync.Mutex]{ 74 m: m.m, 75 T: m.value, 76 }) 77 } else { 78 return resultext.Err[MutexGuard[T, *sync.Mutex], struct{}](struct{}{}) 79 } 80 } 81 82 // RMutexGuard protects the inner contents of a RWMutex2 for safety and unlocking. 83 type RMutexGuard[T any] struct { 84 rw *sync.RWMutex 85 // T is the inner generic type of the Mutex 86 T T 87 } 88 89 // RUnlock unlocks the RWMutex2 value. 90 func (g RMutexGuard[T]) RUnlock() { 91 g.rw.RUnlock() 92 } 93 94 // NewRWMutex2 creates a new RWMutex for use. 95 func NewRWMutex2[T any](value T) RWMutex2[T] { 96 return RWMutex2[T]{ 97 rw: new(sync.RWMutex), 98 value: value, 99 } 100 } 101 102 // RWMutex2 creates a type safe RWMutex wrapper ensuring one cannot access the values of a locked values 103 // without first gaining a lock. 104 type RWMutex2[T any] struct { 105 rw *sync.RWMutex 106 value T 107 } 108 109 // Lock locks the Mutex and returns value for use, safe for mutation if 110 // 111 // If the lock is already in use, the calling goroutine blocks until the mutex is available. 112 func (m RWMutex2[T]) Lock() MutexGuard[T, *sync.RWMutex] { 113 m.rw.Lock() 114 return MutexGuard[T, *sync.RWMutex]{ 115 m: m.rw, 116 T: m.value, 117 } 118 } 119 120 // PerformMut safely locks and unlocks the RWMutex mutable values and performs the provided function. 121 func (m RWMutex2[T]) PerformMut(f func(T)) { 122 guard := m.Lock() 123 f(guard.T) 124 guard.Unlock() 125 } 126 127 // TryLock tries to lock RWMutex and returns the value in the Ok result if successful. 128 // If it does the value is returned for use in the Ok result otherwise Err with empty value. 129 func (m RWMutex2[T]) TryLock() resultext.Result[MutexGuard[T, *sync.RWMutex], struct{}] { 130 if m.rw.TryLock() { 131 return resultext.Ok[MutexGuard[T, *sync.RWMutex], struct{}]( 132 MutexGuard[T, *sync.RWMutex]{ 133 m: m.rw, 134 T: m.value, 135 }) 136 } else { 137 return resultext.Err[MutexGuard[T, *sync.RWMutex], struct{}](struct{}{}) 138 } 139 } 140 141 // Perform safely locks and unlocks the RWMutex read-only values and performs the provided function. 142 func (m RWMutex2[T]) Perform(f func(T)) { 143 guard := m.RLock() 144 f(guard.T) 145 guard.RUnlock() 146 } 147 148 // RLock locks the RWMutex for reading and returns the value for read-only use. 149 // It should not be used for recursive read locking; a blocked Lock call excludes new readers from acquiring the lock 150 func (m RWMutex2[T]) RLock() RMutexGuard[T] { 151 m.rw.RLock() 152 return RMutexGuard[T]{ 153 rw: m.rw, 154 T: m.value, 155 } 156 } 157 158 // TryRLock tries to lock RWMutex for reading and returns the value in the Ok result if successful. 159 // If it does the value is returned for use in the Ok result otherwise Err with empty value. 160 func (m RWMutex2[T]) TryRLock() resultext.Result[RMutexGuard[T], struct{}] { 161 if m.rw.TryRLock() { 162 return resultext.Ok[RMutexGuard[T], struct{}]( 163 RMutexGuard[T]{ 164 rw: m.rw, 165 T: m.value, 166 }, 167 ) 168 } else { 169 return resultext.Err[RMutexGuard[T], struct{}](struct{}{}) 170 } 171 }