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