Source file src/internal/sync/hashtriemap.go

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

View as plain text