aboutsummaryrefslogtreecommitdiff
path: root/src/sync
diff options
context:
space:
mode:
Diffstat (limited to 'src/sync')
-rw-r--r--src/sync/hashtriemap.go116
-rw-r--r--src/sync/map.go2
-rw-r--r--src/sync/map_test.go6
3 files changed, 121 insertions, 3 deletions
diff --git a/src/sync/hashtriemap.go b/src/sync/hashtriemap.go
new file mode 100644
index 0000000000..8df0e2b567
--- /dev/null
+++ b/src/sync/hashtriemap.go
@@ -0,0 +1,116 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build goexperiment.synchashtriemap
+
+package sync
+
+import (
+ isync "internal/sync"
+)
+
+// Map is like a Go map[any]any but is safe for concurrent use
+// by multiple goroutines without additional locking or coordination.
+// Loads, stores, and deletes run in amortized constant time.
+//
+// The Map 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 Map 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 Map may significantly reduce lock
+// contention compared to a Go map paired with a separate [Mutex] or [RWMutex].
+//
+// The zero Map is empty and ready for use. A Map must not be copied after first use.
+//
+// In the terminology of [the Go memory model], Map 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.
+// [Map.Load], [Map.LoadAndDelete], [Map.LoadOrStore], [Map.Swap], [Map.CompareAndSwap],
+// and [Map.CompareAndDelete] are read operations;
+// [Map.Delete], [Map.LoadAndDelete], [Map.Store], and [Map.Swap] are write operations;
+// [Map.LoadOrStore] is a write operation when it returns loaded set to false;
+// [Map.CompareAndSwap] is a write operation when it returns swapped set to true;
+// and [Map.CompareAndDelete] is a write operation when it returns deleted set to true.
+//
+// [the Go memory model]: https://go.dev/ref/mem
+type Map struct {
+ _ noCopy
+
+ m isync.HashTrieMap[any, any]
+}
+
+// Load returns the value stored in the map for a key, or nil if no
+// value is present.
+// The ok result indicates whether value was found in the map.
+func (m *Map) Load(key any) (value any, ok bool) {
+ return m.m.Load(key)
+}
+
+// Store sets the value for a key.
+func (m *Map) Store(key, value any) {
+ m.m.Store(key, value)
+}
+
+// Clear deletes all the entries, resulting in an empty Map.
+func (m *Map) Clear() {
+ m.m.Clear()
+}
+
+// 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.
+func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool) {
+ return m.m.LoadOrStore(key, value)
+}
+
+// LoadAndDelete deletes the value for a key, returning the previous value if any.
+// The loaded result reports whether the key was present.
+func (m *Map) LoadAndDelete(key any) (value any, loaded bool) {
+ return m.m.LoadAndDelete(key)
+}
+
+// Delete deletes the value for a key.
+func (m *Map) Delete(key any) {
+ m.m.Delete(key)
+}
+
+// Swap swaps the value for a key and returns the previous value if any.
+// The loaded result reports whether the key was present.
+func (m *Map) Swap(key, value any) (previous any, loaded bool) {
+ return m.m.Swap(key, value)
+}
+
+// CompareAndSwap swaps the old and new values for key
+// if the value stored in the map is equal to old.
+// The old value must be of a comparable type.
+func (m *Map) CompareAndSwap(key, old, new any) (swapped bool) {
+ return m.m.CompareAndSwap(key, old, new)
+}
+
+// CompareAndDelete deletes the entry for key if its value is equal to old.
+// The old value must be of a comparable type.
+//
+// If there is no current value for key in the map, CompareAndDelete
+// returns false (even if the old value is the nil interface value).
+func (m *Map) CompareAndDelete(key, old any) (deleted bool) {
+ return m.m.CompareAndDelete(key, old)
+}
+
+// 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 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 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 (m *Map) Range(f func(key, value any) bool) {
+ m.m.Range(f)
+}
diff --git a/src/sync/map.go b/src/sync/map.go
index 4f1395110a..25181e0c78 100644
--- a/src/sync/map.go
+++ b/src/sync/map.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !goexperiment.synchashtriemap
+
package sync
import (
diff --git a/src/sync/map_test.go b/src/sync/map_test.go
index cb820e7be2..f12c43a28f 100644
--- a/src/sync/map_test.go
+++ b/src/sync/map_test.go
@@ -358,13 +358,13 @@ func TestConcurrentClear(t *testing.T) {
})
}
-func TestMapClearNoAllocations(t *testing.T) {
+func TestMapClearOneAllocation(t *testing.T) {
testenv.SkipIfOptimizationOff(t)
var m sync.Map
allocs := testing.AllocsPerRun(10, func() {
m.Clear()
})
- if allocs > 0 {
- t.Errorf("AllocsPerRun of m.Clear = %v; want 0", allocs)
+ if allocs > 1 {
+ t.Errorf("AllocsPerRun of m.Clear = %v; want 1", allocs)
}
}