33package syncext
44
55import (
6+ optionext "github.com/go-playground/pkg/v5/values/option"
67 "sync"
78
89 resultext "github.com/go-playground/pkg/v5/values/result"
@@ -22,27 +23,41 @@ type Mutex[T any] struct {
2223 value T
2324}
2425
25- // PerformMut safely locks and unlocks the Mutex values and performs the provided function.
26+ // Lock locks the Mutex and returns value for use, safe for mutation if
2627//
27- // Too bad Go doesn't support PerformMut[R any](func(T) R) R syntax :(
28- func (m * Mutex [T ]) PerformMut (f func (T )) {
29- m .Lock ()
30- defer m .Unlock ()
31- f (m .value )
32- }
33-
34- // Lock locks the Mutex and returns value for mutable use.
3528// If the lock is already in use, the calling goroutine blocks until the mutex is available.
3629func (m * Mutex [T ]) Lock () T {
3730 m .m .Lock ()
3831 return m .value
3932}
4033
41- // Unlock unlocks the Mutex. It is a run-time error if the Mutex is not locked on entry to Unlock.
42- func (m * Mutex [T ]) Unlock () {
34+ // Unlock unlocks the Mutex accepting a value to set as the new or mutated value.
35+ // It is optional to pass a new value to be set but NOT required for there reasons:
36+ // 1. If the internal value is already mutable then no need to set as changes apply as they happen.
37+ // 2. If there's a failure working with the locked value you may NOT want to set it, but still unlock.
38+ // 3. Supports locked values that are not mutable.
39+ //
40+ // It is a run-time error if the Mutex is not locked on entry to Unlock.
41+ func (m * Mutex [T ]) Unlock (value optionext.Option [T ]) {
42+ if value .IsSome () {
43+ m .value = value .Unwrap ()
44+ }
4345 m .m .Unlock ()
4446}
4547
48+ // PerformMut safely locks and unlocks the Mutex values and performs the provided function returning its error if one
49+ // otherwise setting the returned value as the new mutex value.
50+ func (m * Mutex [T ]) PerformMut (f func (T ) (T , error )) error {
51+ value := m .Lock ()
52+ result , err := f (value )
53+ if err != nil {
54+ m .Unlock (optionext .None [T ]())
55+ return err
56+ }
57+ m .Unlock (optionext .Some (result ))
58+ return nil
59+ }
60+
4661// TryLock tries to lock Mutex and reports whether it succeeded.
4762// If it does the value is returned for use in the Ok result otherwise Err with empty value.
4863func (m * Mutex [T ]) TryLock () resultext.Result [T , struct {}] {
@@ -67,26 +82,40 @@ type RWMutex[T any] struct {
6782 value T
6883}
6984
70- // PerformMut safely locks and unlocks the RWMutex mutable values and performs the provided function.
85+ // Lock locks the Mutex and returns value for use, safe for mutation if
7186//
72- // Too bad Go doesn't support PerformMut[R any](func(T) R) R syntax :(
73- func (m * RWMutex [T ]) PerformMut (f func (T )) {
74- m .Lock ()
75- defer m .Unlock ()
76- f (m .value )
77- }
78-
79- // Lock locks mutex and returns values for mutable use.
87+ // If the lock is already in use, the calling goroutine blocks until the mutex is available.
8088func (m * RWMutex [T ]) Lock () T {
8189 m .rw .Lock ()
8290 return m .value
8391}
8492
85- // Unlock unlocks mutable lock for values.
86- func (m * RWMutex [T ]) Unlock () {
93+ // Unlock unlocks the Mutex accepting a value to set as the new or mutated value.
94+ // It is optional to pass a new value to be set but NOT required for there reasons:
95+ // 1. If the internal value is already mutable then no need to set as changes apply as they happen.
96+ // 2. If there's a failure working with the locked value you may NOT want to set it, but still unlock.
97+ // 3. Supports locked values that are not mutable.
98+ //
99+ // It is a run-time error if the Mutex is not locked on entry to Unlock.
100+ func (m * RWMutex [T ]) Unlock (value optionext.Option [T ]) {
101+ if value .IsSome () {
102+ m .value = value .Unwrap ()
103+ }
87104 m .rw .Unlock ()
88105}
89106
107+ // PerformMut safely locks and unlocks the RWMutex mutable values and performs the provided function.
108+ func (m * RWMutex [T ]) PerformMut (f func (T ) (T , error )) error {
109+ value := m .Lock ()
110+ result , err := f (value )
111+ if err != nil {
112+ m .Unlock (optionext .None [T ]())
113+ return err
114+ }
115+ m .Unlock (optionext .Some (result ))
116+ return nil
117+ }
118+
90119// TryLock tries to lock RWMutex and returns the value in the Ok result if successful.
91120// If it does the value is returned for use in the Ok result otherwise Err with empty value.
92121func (m * RWMutex [T ]) TryLock () resultext.Result [T , struct {}] {
@@ -98,12 +127,15 @@ func (m *RWMutex[T]) TryLock() resultext.Result[T, struct{}] {
98127}
99128
100129// Perform safely locks and unlocks the RWMutex read-only values and performs the provided function.
101- //
102- // Too bad Go doesn't support Perform[R any](func(T) R) R syntax :(
103- func (m * RWMutex [T ]) Perform (f func (T )) {
104- m .RLock ()
105- defer m .RUnlock ()
106- f (m .value )
130+ func (m * RWMutex [T ]) Perform (f func (T ) error ) error {
131+ result := m .RLock ()
132+ err := f (result )
133+ if err != nil {
134+ m .RUnlock ()
135+ return err
136+ }
137+ m .RUnlock ()
138+ return nil
107139}
108140
109141// RLock locks the RWMutex for reading and returns the value for read-only use.
0 commit comments