Documentation
¶
Overview ¶
Package gosync provides Map, a generic concurrent map with the semantics of sync.Map and without its cost: no interface boxing of keys and values and no type assertions on the way out.
Map is the hash-trie behind sync.Map since Go 1.24 (internal/sync.HashTrieMap), ported to user code: it hashes keys with hash/maphash.Comparable and locks with sync.Mutex. Loads are lock-free and never allocate; a store allocates one node for the entry, which readers may see immediately.
Map fits the same workloads as sync.Map: caches and registries that are read far more often than written, or where disjoint sets of goroutines write disjoint keys. For write-heavy, high-churn maps see github.com/darxnet/goshard.
ComparableMap adds CompareAndSwap and CompareAndDelete by == for comparable values; Map offers them with an equality function. Map.LoadOrCompute builds a missing value once per key, whatever the number of goroutines asking.
Index ¶
- type ComparableMap
- type Map
- func (m *Map[K, V]) All() iter.Seq2[K, V]
- func (m *Map[K, V]) Clear()
- func (m *Map[K, V]) CompareAndDelete(key K, old V, eq func(current, old V) bool) (deleted bool)
- func (m *Map[K, V]) CompareAndSwap(key K, old, new V, eq func(current, old V) bool) (swapped bool)
- func (m *Map[K, V]) Delete(key K)
- func (m *Map[K, V]) Empty() bool
- func (m *Map[K, V]) Len() int
- func (m *Map[K, V]) Load(key K) (value V, ok bool)
- func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool)
- func (m *Map[K, V]) LoadOrCompute(key K, compute func() V) (actual V, loaded bool)
- func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool)
- func (m *Map[K, V]) Range(f func(K, V) bool)
- func (m *Map[K, V]) Store(key K, value V)
- func (m *Map[K, V]) Swap(key K, value V) (previous V, loaded bool)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ComparableMap ¶
type ComparableMap[K comparable, V comparable] struct { Map[K, V] }
ComparableMap is a Map whose values are comparable, so CompareAndSwap and CompareAndDelete can compare them with == instead of an equality function.
Example (CompareAndDelete) ¶
ExampleComparableMap_compareAndDelete deletes a key only when its value matches the expected one.
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
func main() {
var m gosync.ComparableMap[string, int]
m.Store("session", 42)
fmt.Println(m.CompareAndDelete("session", 0)) // wrong value — no-op
fmt.Println(m.CompareAndDelete("session", 42)) // matches — deleted
_, ok := m.Load("session")
fmt.Println(ok)
}
Output: false true false
Example (CompareAndSwap) ¶
ExampleComparableMap_compareAndSwap demonstrates compare-and-swap by == .
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
func main() {
var m gosync.ComparableMap[string, string]
m.Store("status", "idle")
// Succeeds: current value matches "idle".
fmt.Println(m.CompareAndSwap("status", "idle", "busy"))
// Fails: current value is "busy", not "idle".
fmt.Println(m.CompareAndSwap("status", "idle", "busy"))
}
Output: true false
func (*ComparableMap[K, V]) CompareAndDelete ¶
func (m *ComparableMap[K, V]) CompareAndDelete(key K, old V) (deleted bool)
CompareAndDelete deletes the entry for key if its value is equal to old.
If there is no current value for key in the map, CompareAndDelete returns false.
func (*ComparableMap[K, V]) CompareAndSwap ¶
func (m *ComparableMap[K, V]) CompareAndSwap(key K, old, new V) (swapped bool)
CompareAndSwap swaps the old and new values for key if the value stored in the map is equal to old.
If there is no current value for key in the map, CompareAndSwap returns false.
type Map ¶
type Map[K comparable, V any] struct { // contains filtered or unexported fields }
Map is a concurrent hash-trie with the semantics of sync.Map: it is safe for use by multiple goroutines without additional locking, loads are lock-free, and the iteration order is unspecified.
Map is designed around frequent loads, but offers decent performance for stores and deletes as well, especially if the map is larger.
The zero Map is empty and ready to use. It must not be copied after first use.
Example ¶
ExampleMap demonstrates basic store, load, and delete operations. The zero value is ready to use; no constructor call is required.
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
func main() {
var m gosync.Map[string, int]
m.Store("active_users", 1024)
if v, ok := m.Load("active_users"); ok {
fmt.Println(v)
}
m.Delete("active_users")
if _, ok := m.Load("active_users"); !ok {
fmt.Println("gone")
}
}
Output: 1024 gone
Example (CompareAndSwap) ¶
ExampleMap_compareAndSwap compares values with a function, which lets a Map hold values that are not comparable.
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
func main() {
var m gosync.Map[string, []int]
m.Store("ids", []int{1, 2})
sameLen := func(current, old []int) bool { return len(current) == len(old) }
fmt.Println(m.CompareAndSwap("ids", []int{0, 0}, []int{1, 2, 3}, sameLen))
fmt.Println(m.CompareAndSwap("ids", []int{0, 0}, []int{9}, sameLen))
}
Output: true false
Example (LoadOrCompute) ¶
ExampleMap_loadOrCompute builds the value only when the key is missing, and only once even if several goroutines ask for it at the same time.
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
// Session is the value type of the examples below.
type Session struct {
User string
}
func main() {
var sessions gosync.Map[string, *Session]
s, loaded := sessions.LoadOrCompute("sid-1", func() *Session {
fmt.Println("creating sid-1")
return &Session{User: "ann"}
})
fmt.Println(s.User, loaded)
s, loaded = sessions.LoadOrCompute("sid-1", func() *Session {
fmt.Println("never called")
return nil
})
fmt.Println(s.User, loaded)
}
Output: creating sid-1 ann false ann true
Example (LoadOrStore) ¶
ExampleMap_loadOrStore demonstrates get-or-set semantics: the first call stores the value, the second returns the existing one.
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
func main() {
var m gosync.Map[string, int]
actual, loaded := m.LoadOrStore("limit", 100)
fmt.Println(actual, loaded) // stored
actual, loaded = m.LoadOrStore("limit", 999)
fmt.Println(actual, loaded) // existing value returned
}
Output: 100 false 100 true
func (*Map[K, V]) All ¶
All returns an iterator over each key and value present in the map.
The iterator does not necessarily correspond to any consistent snapshot of the Map's contents: no key will be visited more than once, but if the value for any key is stored or deleted concurrently (including by yield), the iterator may reflect any mapping for that key from any point during iteration. The iterator does not block other methods on the receiver; even yield itself may call any method on the Map.
Example ¶
ExampleMap_All iterates with a range-over-func loop.
package main
import (
"fmt"
"github.com/darxnet/gosync"
)
func main() {
var m gosync.Map[int, string]
m.Store(1, "one")
for k, v := range m.All() {
fmt.Println(k, v)
}
}
Output: 1 one
func (*Map[K, V]) Clear ¶
func (m *Map[K, V]) Clear()
Clear deletes all the entries, resulting in an empty Map.
func (*Map[K, V]) CompareAndDelete ¶
CompareAndDelete deletes the entry for key if its value is equal to old according to eq. The eq function is called while the node of the key is locked.
If there is no current value for key in the map, CompareAndDelete returns false.
func (*Map[K, V]) CompareAndSwap ¶
CompareAndSwap swaps the old and new values for key if the value stored in the map is equal to old according to eq. The eq function is called while the node of the key is locked.
If there is no current value for key in the map, CompareAndSwap returns false.
func (*Map[K, V]) Empty ¶
Empty reports whether the map has no entries. It stops at the first entry found, so it is cheap on a non-empty map.
func (*Map[K, V]) Len ¶
Len returns the number of entries in the map. It walks the whole map, with the same consistency as Range: entries stored or deleted concurrently may or may not be counted.
func (*Map[K, V]) Load ¶
Load returns the value stored in the map for a key, or the zero value if no value is present. The ok result indicates whether value was found in the map.
func (*Map[K, V]) LoadAndDelete ¶
LoadAndDelete deletes the value for a key, returning the previous value if any. The loaded result reports whether the key was present.
func (*Map[K, V]) LoadOrCompute ¶ added in v1.1.0
LoadOrCompute returns the existing value for the key if present. Otherwise, it calls compute, stores its result and returns it. The loaded result is true if the value was loaded, false if computed.
compute runs at most once per key for concurrent callers: the key's bucket is locked while it runs, so it must be short and must not use m. LoadOrStore(key, newValue()) in contrast may build values that are then thrown away.
func (*Map[K, V]) LoadOrStore ¶
LoadOrStore returns the existing value for the key if present. Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored.