Documentation
¶
Overview ¶
Package lck implements thread-safe locking-types, for the Go programming language.
Package lck provides two generic wrappers that serialize concurrent access to a shared value: Lockable for a single value, and Map for a keyed collection of values. Both types are ready to use at their zero value; no constructor is required.
A Lockable holds one value of type T, guarded by a read/write mutex. It supports atomic read, write, exchange, and read-modify-write operations.
A Map holds a keyed collection of values, guarded by a mutex. It supports all the operations of Lockable on a per-key basis, plus bulk operations such as iteration, key enumeration, and clearing.
Both types must not be copied after first use; instead pass them by pointer.
Example usage:
var counter lck.Lockable[int]
counter.Set(0)
var inventory lck.Map[string, int]
inventory.Set("apple", 5)
See also:
Index ¶
- type Lockable
- type Map
- func (receiver *Map[K, V]) Clear()
- func (receiver *Map[K, V]) For(fn func(K, V))
- func (receiver *Map[K, V]) Get(key K) (V, bool)
- func (receiver *Map[K, V]) Has(key K) bool
- func (receiver *Map[K, V]) Keys() []K
- func (receiver *Map[K, V]) Len() int
- func (receiver *Map[K, V]) Let(key K, fn func(V, bool) V) (V, bool)
- func (receiver *Map[K, V]) Set(key K, value V)
- func (receiver *Map[K, V]) Swap(key K, value V) (V, bool)
- func (receiver *Map[K, V]) Unset(key K) (V, bool)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Lockable ¶
type Lockable[T comparable] struct { // contains filtered or unexported fields }
Lockable wraps a single value of type T with a read/write mutex, providing thread-safe read, write, exchange, and read-modify-write operations.
The zero value is ready to use. A Lockable must not be copied after first use; instead pass it by pointer.
Example usage:
var status lck.Lockable[string]
status.Set("ready")
current := status.Get()
See also:
func (*Lockable[T]) Get ¶
func (receiver *Lockable[T]) Get() T
Get returns the current value.
Get acquires a read lock; multiple concurrent Get calls may proceed in parallel. If the receiver is nil, Get returns the zero value of T.
Example usage:
var flag lck.Lockable[bool] flag.Set(true) value := flag.Get()
func (*Lockable[T]) Let ¶
func (receiver *Lockable[T]) Let(fn func(T) T) T
Let atomically replaces the current value with fn(current) and returns the previous value.
The callback funtion fn runs while the write lock is held; fn MUST NOT call any method on the same Lockable — doing so will deadlock. Panics inside fn correctly release the lock.
If the receiver is nil, Let returns the zero value of T and does not call the function fn.
Example usage:
var counter lck.Lockable[int]
prev := counter.Let(func(v int) int {
return v + 1
})
type Map ¶
Map is a thread-safe generic map keyed by K and holding values of type V. All operations are guarded by an internal mutex.
The zero value is ready to use. A Map must not be copied after first use; instead pass it by pointer.
Example usage:
var inventory lck.Map[string, int]
inventory.Set("apple", 5)
count, found := inventory.Get("apple")
See also:
Example (String_int) ¶
package main
import (
"fmt"
"github.com/reiver/go-lck"
)
func main() {
var store lck.Map[string, int]
fmt.Printf("INITIAL-STORE-LEN: %d\n", store.Len())
store.Set("once", 1)
store.Set("twice", 2)
store.Set("thrice", 3)
store.Set("fource", 4)
fmt.Printf("STORE-LEN-AFTER-SETTING: %d\n", store.Len())
value, found := store.Get("twice")
fmt.Printf("STORE[twice]: %d, %t\n", value, found)
fmt.Println()
fmt.Println("KEY-VALUES:")
store.For(func(key string, value int) {
fmt.Println()
fmt.Printf("- KEY: %q\n", key)
fmt.Printf("- VALUE: %d\n", value)
})
store.Unset("twice")
fmt.Println()
fmt.Println("KEY-VALUES:")
store.For(func(key string, value int) {
fmt.Println()
fmt.Printf("- KEY: %q\n", key)
fmt.Printf("- VALUE: %d\n", value)
})
}
Output: INITIAL-STORE-LEN: 0 STORE-LEN-AFTER-SETTING: 4 STORE[twice]: 2, true KEY-VALUES: - KEY: "fource" - VALUE: 4 - KEY: "once" - VALUE: 1 - KEY: "thrice" - VALUE: 3 - KEY: "twice" - VALUE: 2 KEY-VALUES: - KEY: "fource" - VALUE: 4 - KEY: "once" - VALUE: 1 - KEY: "thrice" - VALUE: 3
func (*Map[K, V]) Clear ¶
func (receiver *Map[K, V]) Clear()
Clear removes every entry from the map.
The underlying hash table remains allocated, so subsequent inserts do not pay reallocation cost. If the receiver is nil, or the map has never held entries, Clear does nothing.
Example usage:
var cache lck.Map[string, int]
cache.Set("a", 1)
cache.Clear()
func (*Map[K, V]) For ¶
func (receiver *Map[K, V]) For(fn func(K, V))
For iterates over the map, invoking the function fn once per entry in sorted key order.
For takes a snapshot of the entries while the lock is held, then releases the lock and invokes fn on each snapshot entry. Mutations made by the function fn (or by other goroutines) after the snapshot is taken are not reflected in the current iteration.
If the receiver is nil, For does nothing.
Example usage:
inventory.For(func(key string, value int) {
fmt.Println(key, value)
})
func (*Map[K, V]) Get ¶
Get returns the value stored at key, and a flag indicating whether the key was present.
If the key is absent, or the receiver is nil, Get returns the zero value of V and false.
Example usage:
value, found := inventory.Get("apple")
if !found {
// key is absent
}
func (*Map[K, V]) Has ¶
Has reports whether key is present in the map.
Has is equivalent to discarding the value returned by Map.Get, but it avoids copying the value, which matters when V is large.
If the receiver is nil, Has returns false.
Example usage:
if inventory.Has("apple") {
// key is present
}
func (*Map[K, V]) Keys ¶
func (receiver *Map[K, V]) Keys() []K
Keys returns a snapshot of every key currently in the map.
The returned slice is independent of the map — subsequent mutations do not affect it.
If the receiver is nil, Keys returns nil.
Example usage:
for _, key := range inventory.Keys() {
// ...
}
func (*Map[K, V]) Len ¶
Len returns the number of entries currently in the map.
If the receiver is nil, Len returns 0.
Example usage:
size := inventory.Len()
func (*Map[K, V]) Let ¶
Let atomically replaces the value at key with fn(current, found) and returns the previous value and presence flag.
If key was absent, fn is called with the zero value of V and found=false. Let always writes fn's return value, so after Let returns the key is present with the value fn returned.
The callback fn runs while the write lock is held; fn MUST NOT call any method on the same Map — doing so will deadlock.
If the receiver is nil, Let returns the zero value of V and false, and does not call fn.
Example usage:
prev, had := counters.Let("hits", func(v int, found bool) int {
return v + 1
})
func (*Map[K, V]) Set ¶
func (receiver *Map[K, V]) Set(key K, value V)
Set stores value at key, replacing any previous entry.
If the receiver is nil, Set does nothing.
Example usage:
inventory.Set("apple", 5)
func (*Map[K, V]) Swap ¶
Swap stores value at key and returns the previous value and presence flag.
If the key was absent, Swap returns the zero value of V and false, then inserts the new entry. After Swap returns the key is always present with the supplied value.
If the receiver is nil, Swap returns the zero value of V and false, and does not store anything.
Example usage:
old, found := inventory.Swap("apple", 10)
func (*Map[K, V]) Unset ¶
Unset removes the entry at key and returns its previous value and presence flag.
If the key was absent, Unset returns the zero value of V and false. If the receiver is nil, Unset returns the zero value of V and false, and does not remove anything.
Example usage:
inventory.Unset("apple")
Another example usage:
old, found := inventory.Unset("apple")