mirror of https://github.com/golang/go.git
internal/sync: factor out lookup for CompareAndDelete in HashTrieMap
This lookup will be reused for other operations, like CompareAndSwap. Change-Id: I6698e3c99f7ef6d2b82b9ef489ba8a0be2a71d61 Reviewed-on: https://go-review.googlesource.com/c/go/+/594059 Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
4804d0daca
commit
e51a33a0ef
|
|
@ -181,57 +181,16 @@ func (ht *HashTrieMap[K, V]) expand(oldEntry, newEntry *entry[K, V], newHash uin
|
|||
// (even if the old value is the nil interface value).
|
||||
func (ht *HashTrieMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
|
||||
hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed)
|
||||
var i *indirect[K, V]
|
||||
var hashShift uint
|
||||
var slot *atomic.Pointer[node[K, V]]
|
||||
var n *node[K, V]
|
||||
for {
|
||||
// Find the key or return when there's nothing to delete.
|
||||
i = ht.root
|
||||
hashShift = 8 * goarch.PtrSize
|
||||
found := false
|
||||
for hashShift != 0 {
|
||||
hashShift -= nChildrenLog2
|
||||
|
||||
slot = &i.children[(hash>>hashShift)&nChildrenMask]
|
||||
n = slot.Load()
|
||||
if n == nil {
|
||||
// Nothing to delete. Give up.
|
||||
return
|
||||
}
|
||||
if n.isEntry {
|
||||
// We found an entry. Check if it matches.
|
||||
if _, ok := n.entry().lookup(key, ht.keyEqual); !ok {
|
||||
// No match, nothing to delete.
|
||||
return
|
||||
}
|
||||
// We've got something to delete.
|
||||
found = true
|
||||
break
|
||||
}
|
||||
i = n.indirect()
|
||||
// Find a node with the key and compare with it. n != nil if we found the node.
|
||||
i, hashShift, slot, n := ht.find(key, hash)
|
||||
if n == nil {
|
||||
if i != nil {
|
||||
i.mu.Unlock()
|
||||
}
|
||||
if !found {
|
||||
panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating")
|
||||
}
|
||||
|
||||
// Grab the lock and double-check what we saw.
|
||||
i.mu.Lock()
|
||||
n = slot.Load()
|
||||
if !i.dead.Load() {
|
||||
if n == nil {
|
||||
// Valid node that doesn't contain what we need. Nothing to delete.
|
||||
i.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if n.isEntry {
|
||||
// What we saw is still true, so we can continue with the delete.
|
||||
break
|
||||
}
|
||||
}
|
||||
// We have to start over.
|
||||
i.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// Try to delete the entry.
|
||||
e, deleted := n.entry().compareAndDelete(key, old, ht.keyEqual, ht.valEqual)
|
||||
if !deleted {
|
||||
|
|
@ -268,6 +227,58 @@ func (ht *HashTrieMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
|
|||
return true
|
||||
}
|
||||
|
||||
// compare searches the tree for a node that compares with key (hash must be the hash of key).
|
||||
//
|
||||
// Returns a non-nil node, which will always be an entry, if found.
|
||||
//
|
||||
// If i != nil then i.mu is locked, and it is the caller's responsibility to unlock it.
|
||||
func (ht *HashTrieMap[K, V]) find(key K, hash uintptr) (i *indirect[K, V], hashShift uint, slot *atomic.Pointer[node[K, V]], n *node[K, V]) {
|
||||
for {
|
||||
// Find the key or return when there's nothing to delete.
|
||||
i = ht.root
|
||||
hashShift = 8 * goarch.PtrSize
|
||||
found := false
|
||||
for hashShift != 0 {
|
||||
hashShift -= nChildrenLog2
|
||||
|
||||
slot = &i.children[(hash>>hashShift)&nChildrenMask]
|
||||
n = slot.Load()
|
||||
if n == nil {
|
||||
// Nothing to compare with. Give up.
|
||||
i = nil
|
||||
return
|
||||
}
|
||||
if n.isEntry {
|
||||
// We found an entry. Check if it matches.
|
||||
if _, ok := n.entry().lookup(key, ht.keyEqual); !ok {
|
||||
// No match, comparison failed.
|
||||
i = nil
|
||||
n = nil
|
||||
return
|
||||
}
|
||||
// We've got a match. Prepare to perform an operation on the key.
|
||||
found = true
|
||||
break
|
||||
}
|
||||
i = n.indirect()
|
||||
}
|
||||
if !found {
|
||||
panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating")
|
||||
}
|
||||
|
||||
// Grab the lock and double-check what we saw.
|
||||
i.mu.Lock()
|
||||
n = slot.Load()
|
||||
if !i.dead.Load() && (n == nil || n.isEntry) {
|
||||
// Either we've got a valid node or the node is now nil under the lock.
|
||||
// In either case, we're done here.
|
||||
return
|
||||
}
|
||||
// We have to start over.
|
||||
i.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// All returns an iter.Seq2 that produces all key-value pairs in the map.
|
||||
// The enumeration does not represent any consistent snapshot of the map,
|
||||
// but is guaranteed to visit each unique key-value pair only once. It is
|
||||
|
|
|
|||
Loading…
Reference in New Issue