Documentation
¶
Overview ¶
Package syncutil provides concurrent utilities for safe concurrent programming.
This package offers type-safe wrappers for sync primitives including SyncMap, Mutex wrappers, lazy initialization, and atomic operations.
Examples ¶
// Thread-safe map
var m syncutil.SyncMap[string, int]
m.Store("key", 42)
value := m.Load("key") // Returns Option[int]
if value.IsSome() {
fmt.Println(value.Unwrap()) // Output: 42
}
// Lazy initialization
lazy := syncutil.NewLazy(func() int {
return expensiveComputation()
})
value := lazy.Get() // Computed only once
Index ¶
- Constants
- type AtomicValue
- type LazyValue
- func (o *LazyValue[T]) GetPtr() *T
- func (o *LazyValue[T]) IsInitialized() bool
- func (o *LazyValue[T]) SetInitFunc(onceInit func() result.Result[T]) *LazyValue[T]
- func (o *LazyValue[T]) SetInitValue(v T) *LazyValue[T]
- func (o *LazyValue[T]) SetInitZero() *LazyValue[T]
- func (o *LazyValue[T]) TryGetValue() result.Result[T]
- func (*LazyValue[T]) Zero() T
- type Mutex
- type RWMutex
- func (m *RWMutex[T]) Lock() T
- func (m *RWMutex[T]) LockScope(write func(old T) (new T))
- func (m *RWMutex[T]) RLock() T
- func (m *RWMutex[T]) RLockScope(read func(T))
- func (m *RWMutex[T]) RUnlock()
- func (m *RWMutex[T]) TryBest(readAndDo func(T) bool, swapWhenFalse func(old T) (new option.Option[T]))
- func (m *RWMutex[T]) TryLock() option.Option[T]
- func (m *RWMutex[T]) TryLockScope(write func(old T) (new T))
- func (m *RWMutex[T]) TryRLock() option.Option[T]
- func (m *RWMutex[T]) TryRLockScope(read func(T))
- func (m *RWMutex[T]) Unlock(newData ...T)
- type SyncMap
- func (m *SyncMap[K, V]) Delete(key K)
- func (m *SyncMap[K, V]) Load(key K) option.Option[V]
- func (m *SyncMap[K, V]) LoadAndDelete(key K) (deletedValue option.Option[V])
- func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (existingValue option.Option[V])
- func (m *SyncMap[K, V]) Range(f func(key K, value V) bool)
- func (m *SyncMap[K, V]) Store(key K, value V)
Constants ¶
const ErrLazyValueWithoutInit = "*syncutil.LazyValue[T]: onceInit function is nil"
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AtomicValue ¶
type AtomicValue[T any] struct { // contains filtered or unexported fields }
AtomicValue is a better generic-type wrapper for `atomic.Value`. A AtomicValue provides an atomic load and store of a consistently typed value. The zero value for a AtomicValue returns nil from Load. Once Store has been called, a AtomicValue must not be copied.
A AtomicValue must not be copied after first use.
func (*AtomicValue[T]) CompareAndSwap ¶
func (v *AtomicValue[T]) CompareAndSwap(old T, new T) (swapped bool)
CompareAndSwap executes the compare-and-swap operation for the AtomicValue.
All calls to CompareAndSwap for a given AtomicValue must use values of the same concrete type. CompareAndSwap of an inconsistent type panics, as does CompareAndSwap(old, nil).
func (*AtomicValue[T]) Load ¶
func (v *AtomicValue[T]) Load() (val option.Option[T])
Load returns the value set by the most recent Store. It returns None if there has been no call to Store for this AtomicValue.
func (*AtomicValue[T]) Store ¶
func (v *AtomicValue[T]) Store(val T)
Store sets the value of the AtomicValue to x. All calls to Store for a given AtomicValue must use values of the same concrete type. Store of an inconsistent type panics, as does Store(nil).
func (*AtomicValue[T]) Swap ¶
func (v *AtomicValue[T]) Swap(new T) (old option.Option[T])
Swap stores new into AtomicValue and returns the previous value. It returns None if the AtomicValue is empty.
All calls to Swap for a given AtomicValue must use values of the same concrete type. Swap of an inconsistent type panics, as does Swap(nil).
type LazyValue ¶
type LazyValue[T any] struct { // contains filtered or unexported fields }
LazyValue a value that can be lazily initialized once and read concurrently.
func NewLazyValueWithFunc ¶
NewLazyValueWithFunc new LazyValue with initialization function. The value will be computed lazily when TryGetValue() is called.
func NewLazyValueWithValue ¶
NewLazyValueWithValue new LazyValue with initialization value. The value will be computed lazily when TryGetValue() is called.
func NewLazyValueWithZero ¶
NewLazyValueWithZero new LazyValue with zero. The value will be computed lazily when TryGetValue() is called.
func (*LazyValue[T]) IsInitialized ¶
IsInitialized determine whether it is initialized.
func (*LazyValue[T]) SetInitFunc ¶
SetInitFunc set initialization function. NOTE: onceInit can not be nil If the LazyValue already has an initialization function set (even if not initialized yet), this function will not override it.
func (*LazyValue[T]) SetInitValue ¶
SetInitValue set the initialization value.
func (*LazyValue[T]) SetInitZero ¶
SetInitZero set the zero value for initialization.
func (*LazyValue[T]) TryGetValue ¶
TryGetValue concurrency-safe get the Result[T].
type Mutex ¶
type Mutex[T any] struct { // contains filtered or unexported fields }
Mutex is a better generic-type wrapper for `sync.Mutex` that holds a value. A Mutex is a mutual exclusion lock. The zero value for a Mutex is an unlocked mutex.
A Mutex must not be copied after first use.
In the terminology of the Go memory model, the n'th call to Unlock "synchronizes before" the m'th call to Lock for any n < m. A successful call to TryLock is equivalent to a call to Lock. A failed call to TryLock does not establish any "synchronizes before" relation at all.
func (*Mutex[T]) Lock ¶
func (m *Mutex[T]) Lock() T
Lock locks m. If the lock is already in use, the calling goroutine blocks until the mutex is available.
func (*Mutex[T]) LockScope ¶
func (m *Mutex[T]) LockScope(f func(old T) (new T))
LockScope securely read and write the data in the Mutex[T].
func (*Mutex[T]) TryLock ¶
TryLock tries to lock m and reports whether it succeeded.
Note that while correct uses of TryLock do exist, they are rare, and use of TryLock is often a sign of a deeper problem in a particular use of mutexes.
func (*Mutex[T]) TryLockScope ¶
func (m *Mutex[T]) TryLockScope(f func(old T) (new T))
TryLockScope tries to securely read and write the data in the Mutex[T].
func (*Mutex[T]) Unlock ¶
func (m *Mutex[T]) Unlock(newData ...T)
Unlock unlocks m. It is a run-time error if m is not locked on entry to Unlock.
A locked Mutex is not associated with a particular goroutine. It is allowed for one goroutine to lock a Mutex and then arrange for another goroutine to unlock it.
type RWMutex ¶
type RWMutex[T any] struct { // contains filtered or unexported fields }
RWMutex is a better generic-type wrapper for `sync.RWMutex` that holds a value. A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. The zero value for a RWMutex is an unlocked mutex.
A RWMutex must not be copied after first use.
If a goroutine holds a RWMutex for reading and another goroutine might call Lock, no goroutine should expect to be able to acquire a read lock until the initial read lock is released. In particular, this prohibits recursive read locking. This is to ensure that the lock eventually becomes available; a blocked Lock call excludes new readers from acquiring the lock.
In the terminology of the Go memory model, the n'th call to Unlock "synchronizes before" the m'th call to Lock for any n < m, just as for Mutex. For any call to RLock, there exists an n such that the n'th call to Unlock "synchronizes before" that call to RLock, and the corresponding call to RUnlock "synchronizes before" the n+1'th call to Lock.
func (*RWMutex[T]) Lock ¶
func (m *RWMutex[T]) Lock() T
Lock locks rw for writing. If the lock is already locked for reading or writing, Lock blocks until the lock is available.
func (*RWMutex[T]) LockScope ¶
func (m *RWMutex[T]) LockScope(write func(old T) (new T))
LockScope securely read and write the data in the RWMutex[T].
func (*RWMutex[T]) RLock ¶
func (m *RWMutex[T]) RLock() T
RLock locks rw for reading.
It should not be used for recursive read locking; a blocked Lock call excludes new readers from acquiring the lock. See the documentation on the RWMutex type.
func (*RWMutex[T]) RLockScope ¶
func (m *RWMutex[T]) RLockScope(read func(T))
RLockScope securely read the data in the RWMutex[T].
func (*RWMutex[T]) RUnlock ¶
func (m *RWMutex[T]) RUnlock()
RUnlock undoes a single RLock call; it does not affect other simultaneous readers. It is a run-time error if rw is not locked for reading on entry to RUnlock.
func (*RWMutex[T]) TryBest ¶
func (m *RWMutex[T]) TryBest(readAndDo func(T) bool, swapWhenFalse func(old T) (new option.Option[T]))
TryBest tries to read and do the data in the RWMutex[T] safely, swapping the data when readAndDo returns false and then trying to do again.
func (*RWMutex[T]) TryLock ¶
TryLock tries to lock rw for writing and reports whether it succeeded.
Note that while correct uses of TryLock do exist, they are rare, and use of TryLock is often a sign of a deeper problem
func (*RWMutex[T]) TryLockScope ¶
func (m *RWMutex[T]) TryLockScope(write func(old T) (new T))
TryLockScope tries to securely read and write the data in the RWMutex[T].
func (*RWMutex[T]) TryRLock ¶
TryRLock tries to lock rw for reading and reports whether it succeeded.
Note that while correct uses of TryRLock do exist, they are rare, and use of TryRLock is often a sign of a deeper problem in a particular use of mutexes.
func (*RWMutex[T]) TryRLockScope ¶
func (m *RWMutex[T]) TryRLockScope(read func(T))
TryRLockScope tries to securely read the data in the RWMutex[T].
func (*RWMutex[T]) Unlock ¶
func (m *RWMutex[T]) Unlock(newData ...T)
Unlock unlocks rw for writing. It is a run-time error if rw is not locked for writing on entry to Unlock.
As with Mutexes, a locked RWMutex is not associated with a particular goroutine. One goroutine may RLock (Lock) a RWMutex and then arrange for another goroutine to RUnlock (Unlock) it.
type SyncMap ¶
SyncMap is a better generic-type wrapper for `sync.Map`. A SyncMap is like a Go map[interface{}]interface{} but is safe for concurrent use by multiple goroutines without additional locking or coordination. Loads, stores, and deletes run in amortized constant time.
The SyncMap type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.
The SyncMap type is optimized for two common use cases: (1) when the entry for a given key is only ever written once but read many times, as in caches that only grow, or (2) when multiple goroutines read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a SyncMap may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.
The zero SyncMap is empty and ready for use. A SyncMap must not be copied after first use.
In the terminology of the Go memory model, SyncMap arranges that a write operation "synchronizes before" any read operation that observes the effect of the write, where read and write operations are defined as follows. Load, LoadAndDelete, LoadOrStore are read operations; Delete, LoadAndDelete, and Store are write operations; and LoadOrStore is a write operation when it returns loaded set to false.
func (*SyncMap[K, V]) Delete ¶
func (m *SyncMap[K, V]) Delete(key K)
Delete deletes the value for a key.
func (*SyncMap[K, V]) LoadAndDelete ¶
LoadAndDelete deletes the value for a key, returning the previous value if any.
func (*SyncMap[K, V]) LoadOrStore ¶
LoadOrStore returns the existing value for the key if present. Otherwise, it stores the given value, and returns None.
func (*SyncMap[K, V]) Range ¶
Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration.
Range does not necessarily correspond to any consistent snapshot of the SyncMap's contents: no key will be visited more than once, but if the value for any key is stored or deleted concurrently (including by f), Range may reflect any mapping for that key from any point during the Range call. Range does not block other methods on the receiver; even f itself may call any method on m.
Range may be O(N) with the number of elements in the map even if f returns false after a constant number of calls.