Skip to content

Commit 4050894

Browse files
committed
prevent concurrent deletions on same node
1 parent 5f15ec7 commit 4050894

File tree

2 files changed

+10
-8
lines changed

2 files changed

+10
-8
lines changed

list.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package haxmap
22

33
import "sync/atomic"
44

5-
// marks if a node is deleted or not
6-
const deleted uint32 = 1
5+
// states denoting whether a node is deleted or not
6+
const (
7+
notDeleted uint32 = iota
8+
deleted
9+
)
710

811
// Below implementation is a lock-free linked list based on https://www.cl.cam.ac.uk/research/srg/netos/papers/2001-caslists.pdf by Timothy L. Harris
912
// Performance improvements suggested in https://arxiv.org/pdf/2010.15755.pdf were also added
@@ -97,8 +100,9 @@ func (self *element[K, V]) search(c uintptr, key K) (*element[K, V], *element[K,
97100

98101
// remove marks a node for deletion
99102
// the node will be removed in the next iteration via `element.next()`
100-
func (self *element[K, V]) remove() {
101-
atomic.StoreUint32(&self.deleted, deleted)
103+
// CAS ensures each node can be marked for deletion exactly once
104+
func (self *element[K, V]) remove() bool {
105+
return atomic.CompareAndSwapUint32(&self.deleted, notDeleted, deleted)
102106
}
103107

104108
// if current element is deleted

map.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ func (m *Map[K, V]) Del(keys ...K) {
8989
}
9090
for ; existing != nil && existing.keyHash <= h; existing = existing.next() {
9191
if existing.key == keys[0] {
92-
if !existing.isDeleted() {
93-
existing.remove() // mark node for lazy removal on next pass
92+
if existing.remove() { // mark node for lazy removal on next pass
9493
m.removeItemFromIndex(existing) // remove node from map index
9594
}
9695
return
@@ -118,8 +117,7 @@ func (m *Map[K, V]) Del(keys ...K) {
118117

119118
for elem != nil && iter < size {
120119
if elem.keyHash == delQ[iter].keyHash && elem.key == delQ[iter].key {
121-
if !elem.isDeleted() {
122-
elem.remove() // mark node for lazy removal on next pass
120+
if elem.remove() { // mark node for lazy removal on next pass
123121
m.removeItemFromIndex(elem) // remove node from map index
124122
}
125123
iter++

0 commit comments

Comments
 (0)