syncutil

package
v1.20.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 28, 2025 License: MIT Imports: 4 Imported by: 7

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

View Source
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 NewLazyValue

func NewLazyValue[T any]() *LazyValue[T]

NewLazyValue new empty LazyValue.

func NewLazyValueWithFunc

func NewLazyValueWithFunc[T any](onceInit func() result.Result[T]) *LazyValue[T]

NewLazyValueWithFunc new LazyValue with initialization function. The value will be computed lazily when TryGetValue() is called.

func NewLazyValueWithValue

func NewLazyValueWithValue[T any](v T) *LazyValue[T]

NewLazyValueWithValue new LazyValue with initialization value. The value will be computed lazily when TryGetValue() is called.

func NewLazyValueWithZero

func NewLazyValueWithZero[T any]() *LazyValue[T]

NewLazyValueWithZero new LazyValue with zero. The value will be computed lazily when TryGetValue() is called.

func (*LazyValue[T]) GetPtr

func (o *LazyValue[T]) GetPtr() *T

GetPtr returns its pointer or nil.

func (*LazyValue[T]) IsInitialized

func (o *LazyValue[T]) IsInitialized() bool

IsInitialized determine whether it is initialized.

func (*LazyValue[T]) SetInitFunc

func (o *LazyValue[T]) SetInitFunc(onceInit func() result.Result[T]) *LazyValue[T]

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

func (o *LazyValue[T]) SetInitValue(v T) *LazyValue[T]

SetInitValue set the initialization value.

func (*LazyValue[T]) SetInitZero

func (o *LazyValue[T]) SetInitZero() *LazyValue[T]

SetInitZero set the zero value for initialization.

func (*LazyValue[T]) TryGetValue

func (o *LazyValue[T]) TryGetValue() result.Result[T]

TryGetValue concurrency-safe get the Result[T].

func (*LazyValue[T]) Zero

func (*LazyValue[T]) Zero() T

Zero creates a zero 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 NewMutex

func NewMutex[T any](data T) *Mutex[T]

NewMutex returns a new *Mutex.

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

func (m *Mutex[T]) TryLock() option.Option[T]

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 NewRWMutex

func NewRWMutex[T any](data T) *RWMutex[T]

NewRWMutex returns a new *RWMutex.

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

func (m *RWMutex[T]) TryLock() option.Option[T]

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

func (m *RWMutex[T]) TryRLock() option.Option[T]

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

type SyncMap[K any, V any] struct {
	// contains filtered or unexported fields
}

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]) Load

func (m *SyncMap[K, V]) Load(key K) option.Option[V]

Load returns the value stored in the map for a key.

func (*SyncMap[K, V]) LoadAndDelete

func (m *SyncMap[K, V]) LoadAndDelete(key K) (deletedValue option.Option[V])

LoadAndDelete deletes the value for a key, returning the previous value if any.

func (*SyncMap[K, V]) LoadOrStore

func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (existingValue option.Option[V])

LoadOrStore returns the existing value for the key if present. Otherwise, it stores the given value, and returns None.

func (*SyncMap[K, V]) Range

func (m *SyncMap[K, V]) Range(f func(key K, value V) bool)

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.

func (*SyncMap[K, V]) Store

func (m *SyncMap[K, V]) Store(key K, value V)

Store sets the value for a key.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL