已合并
internal/sync: Add support for wide trie in sync.HashTrieMap #51
spy20051623创建于 1月28日
internal/sync: Add support for wide trie in sync.HashTrieMap #51
已合并
spy20051623创建于 1月28日
共 5 个文件变更+745-0
@@ -0,0 +1,8 @@
1+// Code generated by mkconsts.go. DO NOT EDIT.
2+ 
3+//go:build !goexperiment.widetrie
4+ 
5+package goexperiment
6+ 
7+const WideTrie = false
8+const WideTrieInt = 0
@@ -0,0 +1,8 @@
1+// Code generated by mkconsts.go. DO NOT EDIT.
2+ 
3+//go:build goexperiment.widetrie
4+ 
5+package goexperiment
6+ 
7+const WideTrie = true
8+const WideTrieInt = 1
@@ -128,4 +128,7 @@ type Flags struct {
128 128 
129 // Synctest enables the testing/synctest package.129 // Synctest enables the testing/synctest package.
130 Synctest bool130 Synctest bool
131+ 
132+ // WideTrie uses larger children group in sync.hashtriemap
133+ WideTrie bool
131}134}
@@ -1,6 +1,7 @@
1// Copyright 2024 The Go Authors. All rights reserved.1// Copyright 2024 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.3// license that can be found in the LICENSE file.
4+//go:build !goexperiment.widetrie
4 5 
5package sync6package sync
6 7 
@@ -0,0 +1,725 @@
1+// Copyright 2024 The Go Authors. All rights reserved.
2+// Use of this source code is governed by a BSD-style
3+// license that can be found in the LICENSE file.
4+//go:build goexperiment.widetrie
5+ 
6+package sync
7+ 
8+import (
9+ "internal/abi"
10+ "internal/goarch"
11+ "sync/atomic"
12+ "unsafe"
13+)
14+ 
15+// HashTrieMap is an implementation of a concurrent hash-trie. The implementation
16+// is designed around frequent loads, but offers decent performance for stores
17+// and deletes as well, especially if the map is larger. Its primary use-case is
18+// the unique package, but can be used elsewhere as well.
19+//
20+// The zero HashTrieMap is empty and ready to use.
21+// It must not be copied after first use.
22+type HashTrieMap[K comparable, V any] struct {
23+ inited atomic.Uint32
24+ initMu Mutex
25+ root atomic.Pointer[indirect[K, V]]
26+ keyHash hashFunc
27+ valEqual equalFunc
28+ seed uintptr
29+}
30+ 
31+func (ht *HashTrieMap[K, V]) init() {
32+ if ht.inited.Load() == 0 {
33+ ht.initSlow()
34+ }
35+}
36+ 
37+//go:noinline
38+func (ht *HashTrieMap[K, V]) initSlow() {
39+ ht.initMu.Lock()
40+ defer ht.initMu.Unlock()
41+ 
42+ if ht.inited.Load() != 0 {
43+ // Someone got to it while we were waiting.
44+ return
45+ }
46+ 
47+ // Set up root node, derive the hash function for the key, and the
48+ // equal function for the value, if any.
49+ var m map[K]V
50+ mapType := abi.TypeOf(m).MapType()
51+ ht.root.Store(newIndirectNode[K, V](nil))
52+ ht.keyHash = mapType.Hasher
53+ ht.valEqual = mapType.Elem.Equal
54+ ht.seed = uintptr(runtime_rand())
55+ 
56+ ht.inited.Store(1)
57+}
58+ 
59+type hashFunc func(unsafe.Pointer, uintptr) uintptr
60+type equalFunc func(unsafe.Pointer, unsafe.Pointer) bool
61+ 
62+// Load returns the value stored in the map for a key, or nil if no
63+// value is present.
64+// The ok result indicates whether value was found in the map.
65+func (ht *HashTrieMap[K, V]) Load(key K) (value V, ok bool) {
66+ ht.init()
67+ hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
68+ 
69+ i := ht.root.Load()
70+ hashShift := 8 * goarch.PtrSize
71+ for hashShift >= nChildrenLog2 {
72+ hashShift -= nChildrenLog2
73+ 
74+ n := i.children[(hash>>hashShift)&nChildrenMask].Load()
75+ if n == nil {
76+ return *new(V), false
77+ }
78+ if n.isEntry {
79+ return n.entry().lookup(key)
80+ }
81+ i = n.indirect()
82+ }
83+ panic("internal/sync.HashTrieMap: ran out of hash bits while iterating")
84+}
85+ 
86+// LoadOrStore returns the existing value for the key if present.
87+// Otherwise, it stores and returns the given value.
88+// The loaded result is true if the value was loaded, false if stored.
89+func (ht *HashTrieMap[K, V]) LoadOrStore(key K, value V) (result V, loaded bool) {
90+ ht.init()
91+ hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
92+ var i *indirect[K, V]
93+ var hashShift uint
94+ var slot *atomic.Pointer[node[K, V]]
95+ var n *node[K, V]
96+ for {
97+ // Find the key or a candidate location for insertion.
98+ i = ht.root.Load()
99+ hashShift = 8 * goarch.PtrSize
100+ haveInsertPoint := false
101+ for hashShift >= nChildrenLog2 {
102+ hashShift -= nChildrenLog2
103+ 
104+ slot = &i.children[(hash>>hashShift)&nChildrenMask]
105+ n = slot.Load()
106+ if n == nil {
107+ // We found a nil slot which is a candidate for insertion.
108+ haveInsertPoint = true
109+ break
110+ }
111+ if n.isEntry {
112+ // We found an existing entry, which is as far as we can go.
113+ // If it stays this way, we'll have to replace it with an
114+ // indirect node.
115+ if v, ok := n.entry().lookup(key); ok {
116+ return v, true
117+ }
118+ haveInsertPoint = true
119+ break
120+ }
121+ i = n.indirect()
122+ }
123+ if !haveInsertPoint {
124+ panic("internal/sync.HashTrieMap: ran out of hash bits while iterating")
125+ }
126+ 
127+ // Grab the lock and double-check what we saw.
128+ i.mu.Lock()
129+ n = slot.Load()
130+ if (n == nil || n.isEntry) && !i.dead.Load() {
131+ // What we saw is still true, so we can continue with the insert.
132+ break
133+ }
134+ // We have to start over.
135+ i.mu.Unlock()
136+ }
137+ // N.B. This lock is held from when we broke out of the outer loop above.
138+ // We specifically break this out so that we can use defer here safely.
139+ // One option is to break this out into a new function instead, but
140+ // there's so much local iteration state used below that this turns out
141+ // to be cleaner.
142+ defer i.mu.Unlock()
143+ 
144+ var oldEntry *entry[K, V]
145+ if n != nil {
146+ oldEntry = n.entry()
147+ if v, ok := oldEntry.lookup(key); ok {
148+ // Easy case: by loading again, it turns out exactly what we wanted is here!
149+ return v, true
150+ }
151+ }
152+ newEntry := newEntryNode(key, value)
153+ if oldEntry == nil {
154+ // Easy case: create a new entry and store it.
155+ slot.Store(&newEntry.node)
156+ } else {
157+ // We possibly need to expand the entry already there into one or more new nodes.
158+ //
159+ // Publish the node last, which will make both oldEntry and newEntry visible. We
160+ // don't want readers to be able to observe that oldEntry isn't in the tree.
161+ slot.Store(ht.expand(oldEntry, newEntry, hash, hashShift, i))
162+ }
163+ return value, false
164+}
165+ 
166+// expand takes oldEntry and newEntry whose hashes conflict from bit 64 down to hashShift and
167+// produces a subtree of indirect nodes to hold the two new entries.
168+func (ht *HashTrieMap[K, V]) expand(oldEntry, newEntry *entry[K, V], newHash uintptr, hashShift uint, parent *indirect[K, V]) *node[K, V] {
169+ // Check for a hash collision.
170+ oldHash := ht.keyHash(unsafe.Pointer(&oldEntry.key), ht.seed)
171+ if oldHash == newHash {
172+ // Store the old entry in the new entry's overflow list, then store
173+ // the new entry.
174+ newEntry.overflow.Store(oldEntry)
175+ return &newEntry.node
176+ }
177+ // We have to add an indirect node. Worse still, we may need to add more than one.
178+ newIndirect := newIndirectNode(parent)
179+ top := newIndirect
180+ for {
181+ if hashShift < nChildrenLog2 {
182+ panic("internal/sync.HashTrieMap: ran out of hash bits while inserting")
183+ }
184+ hashShift -= nChildrenLog2 // hashShift is for the level parent is at. We need to go deeper.
185+ oi := (oldHash >> hashShift) & nChildrenMask
186+ ni := (newHash >> hashShift) & nChildrenMask
187+ if oi != ni {
188+ newIndirect.children[oi].Store(&oldEntry.node)
189+ newIndirect.children[ni].Store(&newEntry.node)
190+ break
191+ }
192+ nextIndirect := newIndirectNode(newIndirect)
193+ newIndirect.children[oi].Store(&nextIndirect.node)
194+ newIndirect = nextIndirect
195+ }
196+ return &top.node
197+}
198+ 
199+// Store sets the value for a key.
200+func (ht *HashTrieMap[K, V]) Store(key K, old V) {
201+ _, _ = ht.Swap(key, old)
202+}
203+ 
204+// Swap swaps the value for a key and returns the previous value if any.
205+// The loaded result reports whether the key was present.
206+func (ht *HashTrieMap[K, V]) Swap(key K, new V) (previous V, loaded bool) {
207+ ht.init()
208+ hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
209+ var i *indirect[K, V]
210+ var hashShift uint
211+ var slot *atomic.Pointer[node[K, V]]
212+ var n *node[K, V]
213+ for {
214+ // Find the key or a candidate location for insertion.
215+ i = ht.root.Load()
216+ hashShift = 8 * goarch.PtrSize
217+ haveInsertPoint := false
218+ for hashShift >= nChildrenLog2 {
219+ hashShift -= nChildrenLog2
220+ 
221+ slot = &i.children[(hash>>hashShift)&nChildrenMask]
222+ n = slot.Load()
223+ if n == nil || n.isEntry {
224+ // We found a nil slot which is a candidate for insertion,
225+ // or an existing entry that we'll replace.
226+ haveInsertPoint = true
227+ break
228+ }
229+ i = n.indirect()
230+ }
231+ if !haveInsertPoint {
232+ panic("internal/sync.HashTrieMap: ran out of hash bits while iterating")
233+ }
234+ 
235+ // Grab the lock and double-check what we saw.
236+ i.mu.Lock()
237+ n = slot.Load()
238+ if (n == nil || n.isEntry) && !i.dead.Load() {
239+ // What we saw is still true, so we can continue with the insert.
240+ break
241+ }
242+ // We have to start over.
243+ i.mu.Unlock()
244+ }
245+ // N.B. This lock is held from when we broke out of the outer loop above.
246+ // We specifically break this out so that we can use defer here safely.
247+ // One option is to break this out into a new function instead, but
248+ // there's so much local iteration state used below that this turns out
249+ // to be cleaner.
250+ defer i.mu.Unlock()
251+ 
252+ var zero V
253+ var oldEntry *entry[K, V]
254+ if n != nil {
255+ // Swap if the keys compare.
256+ oldEntry = n.entry()
257+ newEntry, old, swapped := oldEntry.swap(key, new)
258+ if swapped {
259+ slot.Store(&newEntry.node)
260+ return old, true
261+ }
262+ }
263+ // The keys didn't compare, so we're doing an insertion.
264+ newEntry := newEntryNode(key, new)
265+ if oldEntry == nil {
266+ // Easy case: create a new entry and store it.
267+ slot.Store(&newEntry.node)
268+ } else {
269+ // We possibly need to expand the entry already there into one or more new nodes.
270+ //
271+ // Publish the node last, which will make both oldEntry and newEntry visible. We
272+ // don't want readers to be able to observe that oldEntry isn't in the tree.
273+ slot.Store(ht.expand(oldEntry, newEntry, hash, hashShift, i))
274+ }
275+ return zero, false
276+}
277+ 
278+// CompareAndSwap swaps the old and new values for key
279+// if the value stored in the map is equal to old.
280+// The value type must be of a comparable type, otherwise CompareAndSwap will panic.
281+func (ht *HashTrieMap[K, V]) CompareAndSwap(key K, old, new V) (swapped bool) {
282+ ht.init()
283+ if ht.valEqual == nil {
284+ panic("called CompareAndSwap when value is not of comparable type")
285+ }
286+ hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
287+ 
288+ // Find a node with the key and compare with it. n != nil if we found the node.
289+ i, _, slot, n := ht.find(key, hash, ht.valEqual, old)
290+ if i != nil {
291+ defer i.mu.Unlock()
292+ }
293+ if n == nil {
294+ return false
295+ }
296+ 
297+ // Try to swap the entry.
298+ e, swapped := n.entry().compareAndSwap(key, old, new, ht.valEqual)
299+ if !swapped {
300+ // Nothing was actually swapped, which means the node is no longer there.
301+ return false
302+ }
303+ // Store the entry back because it changed.
304+ slot.Store(&e.node)
305+ return true
306+}
307+ 
308+// LoadAndDelete deletes the value for a key, returning the previous value if any.
309+// The loaded result reports whether the key was present.
310+func (ht *HashTrieMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
311+ ht.init()
312+ hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
313+ 
314+ // Find a node with the key and compare with it. n != nil if we found the node.
315+ i, hashShift, slot, n := ht.find(key, hash, nil, *new(V))
316+ if n == nil {
317+ if i != nil {
318+ i.mu.Unlock()
319+ }
320+ return *new(V), false
321+ }
322+ 
323+ // Try to delete the entry.
324+ v, e, loaded := n.entry().loadAndDelete(key)
325+ if !loaded {
326+ // Nothing was actually deleted, which means the node is no longer there.
327+ i.mu.Unlock()
328+ return *new(V), false
329+ }
330+ if e != nil {
331+ // We didn't actually delete the whole entry, just one entry in the chain.
332+ // Nothing else to do, since the parent is definitely not empty.
333+ slot.Store(&e.node)
334+ i.mu.Unlock()
335+ return v, true
336+ }
337+ // Delete the entry.
338+ slot.Store(nil)
339+ 
340+ // Check if the node is now empty (and isn't the root), and delete it if able.
341+ for i.parent != nil && i.empty() {
342+ if hashShift == 8*goarch.PtrSize {
343+ panic("internal/sync.HashTrieMap: ran out of hash bits while iterating")
344+ }
345+ hashShift += nChildrenLog2
346+ 
347+ // Delete the current node in the parent.
348+ parent := i.parent
349+ parent.mu.Lock()
350+ i.dead.Store(true)
351+ parent.children[(hash>>hashShift)&nChildrenMask].Store(nil)
352+ i.mu.Unlock()
353+ i = parent
354+ }
355+ i.mu.Unlock()
356+ return v, true
357+}
358+ 
359+// Delete deletes the value for a key.
360+func (ht *HashTrieMap[K, V]) Delete(key K) {
361+ _, _ = ht.LoadAndDelete(key)
362+}
363+ 
364+// CompareAndDelete deletes the entry for key if its value is equal to old.
365+// The value type must be comparable, otherwise this CompareAndDelete will panic.
366+//
367+// If there is no current value for key in the map, CompareAndDelete returns false
368+// (even if the old value is the nil interface value).
369+func (ht *HashTrieMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
370+ ht.init()
371+ if ht.valEqual == nil {
372+ panic("called CompareAndDelete when value is not of comparable type")
373+ }
374+ hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
375+ 
376+ // Find a node with the key. n != nil if we found the node.
377+ i, hashShift, slot, n := ht.find(key, hash, nil, *new(V))
378+ if n == nil {
379+ if i != nil {
380+ i.mu.Unlock()
381+ }
382+ return false
383+ }
384+ 
385+ // Try to delete the entry.
386+ e, deleted := n.entry().compareAndDelete(key, old, ht.valEqual)
387+ if !deleted {
388+ // Nothing was actually deleted, which means the node is no longer there.
389+ i.mu.Unlock()
390+ return false
391+ }
392+ if e != nil {
393+ // We didn't actually delete the whole entry, just one entry in the chain.
394+ // Nothing else to do, since the parent is definitely not empty.
395+ slot.Store(&e.node)
396+ i.mu.Unlock()
397+ return true
398+ }
399+ // Delete the entry.
400+ slot.Store(nil)
401+ 
402+ // Check if the node is now empty (and isn't the root), and delete it if able.
403+ for i.parent != nil && i.empty() {
404+ if hashShift == 8*goarch.PtrSize {
405+ panic("internal/sync.HashTrieMap: ran out of hash bits while iterating")
406+ }
407+ hashShift += nChildrenLog2
408+ 
409+ // Delete the current node in the parent.
410+ parent := i.parent
411+ parent.mu.Lock()
412+ i.dead.Store(true)
413+ parent.children[(hash>>hashShift)&nChildrenMask].Store(nil)
414+ i.mu.Unlock()
415+ i = parent
416+ }
417+ i.mu.Unlock()
418+ return true
419+}
420+ 
421+// find searches the tree for a node that contains key (hash must be the hash of key).
422+// If valEqual != nil, then it will also enforce that the values are equal as well.
423+//
424+// Returns a non-nil node, which will always be an entry, if found.
425+//
426+// If i != nil then i.mu is locked, and it is the caller's responsibility to unlock it.
427+func (ht *HashTrieMap[K, V]) find(key K, hash uintptr, valEqual equalFunc, value V) (i *indirect[K, V], hashShift uint, slot *atomic.Pointer[node[K, V]], n *node[K, V]) {
428+ for {
429+ // Find the key or return if it's not there.
430+ i = ht.root.Load()
431+ hashShift = 8 * goarch.PtrSize
432+ found := false
433+ for hashShift >= nChildrenLog2 {
434+ hashShift -= nChildrenLog2
435+ 
436+ slot = &i.children[(hash>>hashShift)&nChildrenMask]
437+ n = slot.Load()
438+ if n == nil {
439+ // Nothing to compare with. Give up.
440+ i = nil
441+ return
442+ }
443+ if n.isEntry {
444+ // We found an entry. Check if it matches.
445+ if _, ok := n.entry().lookupWithValue(key, value, valEqual); !ok {
446+ // No match, comparison failed.
447+ i = nil
448+ n = nil
449+ return
450+ }
451+ // We've got a match. Prepare to perform an operation on the key.
452+ found = true
453+ break
454+ }
455+ i = n.indirect()
456+ }
457+ if !found {
458+ panic("internal/sync.HashTrieMap: ran out of hash bits while iterating")
459+ }
460+ 
461+ // Grab the lock and double-check what we saw.
462+ i.mu.Lock()
463+ n = slot.Load()
464+ if !i.dead.Load() && (n == nil || n.isEntry) {
465+ // Either we've got a valid node or the node is now nil under the lock.
466+ // In either case, we're done here.
467+ return
468+ }
469+ // We have to start over.
470+ i.mu.Unlock()
471+ }
472+}
473+ 
474+// All returns an iterator over each key and value present in the map.
475+//
476+// The iterator does not necessarily correspond to any consistent snapshot of the
477+// HashTrieMap's contents: no key will be visited more than once, but if the value
478+// for any key is stored or deleted concurrently (including by yield), the iterator
479+// may reflect any mapping for that key from any point during iteration. The iterator
480+// does not block other methods on the receiver; even yield itself may call any
481+// method on the HashTrieMap.
482+func (ht *HashTrieMap[K, V]) All() func(yield func(K, V) bool) {
483+ ht.init()
484+ return func(yield func(key K, value V) bool) {
485+ ht.iter(ht.root.Load(), yield)
486+ }
487+}
488+ 
489+// Range calls f sequentially for each key and value present in the map.
490+// If f returns false, range stops the iteration.
491+//
492+// This exists for compatibility with sync.Map; All should be preferred.
493+// It provides the same guarantees as sync.Map, and All.
494+func (ht *HashTrieMap[K, V]) Range(yield func(K, V) bool) {
495+ ht.init()
496+ ht.iter(ht.root.Load(), yield)
497+}
498+ 
499+func (ht *HashTrieMap[K, V]) iter(i *indirect[K, V], yield func(key K, value V) bool) bool {
500+ for j := range i.children {
501+ n := i.children[j].Load()
502+ if n == nil {
503+ continue
504+ }
505+ if !n.isEntry {
506+ if !ht.iter(n.indirect(), yield) {
507+ return false
508+ }
509+ continue
510+ }
511+ e := n.entry()
512+ for e != nil {
513+ if !yield(e.key, e.value) {
514+ return false
515+ }
516+ e = e.overflow.Load()
517+ }
518+ }
519+ return true
520+}
521+ 
522+// Clear deletes all the entries, resulting in an empty HashTrieMap.
523+func (ht *HashTrieMap[K, V]) Clear() {
524+ ht.init()
525+ 
526+ // It's sufficient to just drop the root on the floor, but the root
527+ // must always be non-nil.
528+ ht.root.Store(newIndirectNode[K, V](nil))
529+}
530+ 
531+const (
532+ // 16 children. This seems to be the sweet spot for
533+ // load performance: any smaller and we lose out on
534+ // 50% or more in CPU performance. Any larger and the
535+ // returns are minuscule (~1% improvement for 32 children).
536+ nChildrenLog2 = 7
537+ nChildren = 1 << nChildrenLog2
538+ nChildrenMask = nChildren - 1
539+)
540+ 
541+// indirect is an internal node in the hash-trie.
542+type indirect[K comparable, V any] struct {
543+ node[K, V]
544+ dead atomic.Bool
545+ mu Mutex // Protects mutation to children and any children that are entry nodes.
546+ parent *indirect[K, V]
547+ children [nChildren]atomic.Pointer[node[K, V]]
548+}
549+ 
550+func newIndirectNode[K comparable, V any](parent *indirect[K, V]) *indirect[K, V] {
551+ return &indirect[K, V]{node: node[K, V]{isEntry: false}, parent: parent}
552+}
553+ 
554+func (i *indirect[K, V]) empty() bool {
555+ nc := 0
556+ for j := range i.children {
557+ if i.children[j].Load() != nil {
558+ nc++
559+ }
560+ }
561+ return nc == 0
562+}
563+ 
564+// entry is a leaf node in the hash-trie.
565+type entry[K comparable, V any] struct {
566+ node[K, V]
567+ overflow atomic.Pointer[entry[K, V]] // Overflow for hash collisions.
568+ key K
569+ value V
570+}
571+ 
572+func newEntryNode[K comparable, V any](key K, value V) *entry[K, V] {
573+ return &entry[K, V]{
574+ node: node[K, V]{isEntry: true},
575+ key: key,
576+ value: value,
577+ }
578+}
579+ 
580+func (e *entry[K, V]) lookup(key K) (V, bool) {
581+ for e != nil {
582+ if e.key == key {
583+ return e.value, true
584+ }
585+ e = e.overflow.Load()
586+ }
587+ return *new(V), false
588+}
589+ 
590+func (e *entry[K, V]) lookupWithValue(key K, value V, valEqual equalFunc) (V, bool) {
591+ for e != nil {
592+ if e.key == key && (valEqual == nil || valEqual(unsafe.Pointer(&e.value), abi.NoEscape(unsafe.Pointer(&value)))) {
593+ return e.value, true
594+ }
595+ e = e.overflow.Load()
596+ }
597+ return *new(V), false
598+}
599+ 
600+// swap replaces an entry in the overflow chain if keys compare equal. Returns the new entry chain,
601+// the old value, and whether or not anything was swapped.
602+//
603+// swap must be called under the mutex of the indirect node which e is a child of.
604+func (head *entry[K, V]) swap(key K, new V) (*entry[K, V], V, bool) {
605+ if head.key == key {
606+ // Return the new head of the list.
607+ e := newEntryNode(key, new)
608+ if chain := head.overflow.Load(); chain != nil {
609+ e.overflow.Store(chain)
610+ }
611+ return e, head.value, true
612+ }
613+ i := &head.overflow
614+ e := i.Load()
615+ for e != nil {
616+ if e.key == key {
617+ eNew := newEntryNode(key, new)
618+ eNew.overflow.Store(e.overflow.Load())
619+ i.Store(eNew)
620+ return head, e.value, true
621+ }
622+ i = &e.overflow
623+ e = e.overflow.Load()
624+ }
625+ var zero V
626+ return head, zero, false
627+}
628+ 
629+// compareAndSwap replaces an entry in the overflow chain if both the key and value compare
630+// equal. Returns the new entry chain and whether or not anything was swapped.
631+//
632+// compareAndSwap must be called under the mutex of the indirect node which e is a child of.
633+func (head *entry[K, V]) compareAndSwap(key K, old, new V, valEqual equalFunc) (*entry[K, V], bool) {
634+ if head.key == key && valEqual(unsafe.Pointer(&head.value), abi.NoEscape(unsafe.Pointer(&old))) {
635+ // Return the new head of the list.
636+ e := newEntryNode(key, new)
637+ if chain := head.overflow.Load(); chain != nil {
638+ e.overflow.Store(chain)
639+ }
640+ return e, true
641+ }
642+ i := &head.overflow
643+ e := i.Load()
644+ for e != nil {
645+ if e.key == key && valEqual(unsafe.Pointer(&e.value), abi.NoEscape(unsafe.Pointer(&old))) {
646+ eNew := newEntryNode(key, new)
647+ eNew.overflow.Store(e.overflow.Load())
648+ i.Store(eNew)
649+ return head, true
650+ }
651+ i = &e.overflow
652+ e = e.overflow.Load()
653+ }
654+ return head, false
655+}
656+ 
657+// loadAndDelete deletes an entry in the overflow chain by key. Returns the value for the key, the new
658+// entry chain and whether or not anything was loaded (and deleted).
659+//
660+// loadAndDelete must be called under the mutex of the indirect node which e is a child of.
661+func (head *entry[K, V]) loadAndDelete(key K) (V, *entry[K, V], bool) {
662+ if head.key == key {
663+ // Drop the head of the list.
664+ return head.value, head.overflow.Load(), true
665+ }
666+ i := &head.overflow
667+ e := i.Load()
668+ for e != nil {
669+ if e.key == key {
670+ i.Store(e.overflow.Load())
671+ return e.value, head, true
672+ }
673+ i = &e.overflow
674+ e = e.overflow.Load()
675+ }
676+ return *new(V), head, false
677+}
678+ 
679+// compareAndDelete deletes an entry in the overflow chain if both the key and value compare
680+// equal. Returns the new entry chain and whether or not anything was deleted.
681+//
682+// compareAndDelete must be called under the mutex of the indirect node which e is a child of.
683+func (head *entry[K, V]) compareAndDelete(key K, value V, valEqual equalFunc) (*entry[K, V], bool) {
684+ if head.key == key && valEqual(unsafe.Pointer(&head.value), abi.NoEscape(unsafe.Pointer(&value))) {
685+ // Drop the head of the list.
686+ return head.overflow.Load(), true
687+ }
688+ i := &head.overflow
689+ e := i.Load()
690+ for e != nil {
691+ if e.key == key && valEqual(unsafe.Pointer(&e.value), abi.NoEscape(unsafe.Pointer(&value))) {
692+ i.Store(e.overflow.Load())
693+ return head, true
694+ }
695+ i = &e.overflow
696+ e = e.overflow.Load()
697+ }
698+ return head, false
699+}
700+ 
701+// node is the header for a node. It's polymorphic and
702+// is actually either an entry or an indirect.
703+type node[K comparable, V any] struct {
704+ isEntry bool
705+}
706+ 
707+func (n *node[K, V]) entry() *entry[K, V] {
708+ if !n.isEntry {
709+ panic("called entry on non-entry node")
710+ }
711+ return (*entry[K, V])(unsafe.Pointer(n))
712+}
713+ 
714+func (n *node[K, V]) indirect() *indirect[K, V] {
715+ if n.isEntry {
716+ panic("called indirect on entry node")
717+ }
718+ return (*indirect[K, V])(unsafe.Pointer(n))
719+}
720+ 
721+// Pull in runtime.rand so that we don't need to take a dependency
722+// on math/rand/v2.
723+//
724+//go:linkname runtime_rand runtime.rand
725+func runtime_rand() uint64