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  }