Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions hashmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func (m *Map[Key, Value]) Get(key Key) (Value, bool) {
hash := m.hasher(key)

for element := m.store.Load().item(hash); element != nil; element = element.Next() {
if element.deleted.Load() != 0 {
continue
}

if element.keyHash == hash && element.key == key {
return element.Value(), true
}
Expand Down
24 changes: 24 additions & 0 deletions hashmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,27 @@ func TestGetOrInsertHangIssue67(_ *testing.T) {

wg.Wait()
}

func TestGetOrInsertConcurrentIssue81(t *testing.T) {
m := New[int, bool]()
var wg sync.WaitGroup
n := 1000

for i := range n {
wg.Add(1)
go func(val int) {
defer wg.Done()
m.GetOrInsert(val, true)
}(i)
}
wg.Wait()

count := 0
m.Range(func(_ int, value bool) bool {
count++
return true
})

assert.Equal(t, n, count)
assert.Equal(t, uintptr(n), m.Len())
}
15 changes: 13 additions & 2 deletions list.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (l *List[Key, Value]) Delete(element *ListElement[Key, Value]) {
}

func (l *List[Key, Value]) search(searchStart *ListElement[Key, Value], hash uintptr, key Key) (left, found, right *ListElement[Key, Value]) {
if searchStart != nil && hash < searchStart.keyHash { // key would remain left from item? {
if searchStart != nil && (hash < searchStart.keyHash || searchStart.deleted.Load() != 0) { // key would remain left from item or item deleted?
searchStart = nil // start search at head
}

Expand All @@ -92,7 +92,9 @@ func (l *List[Key, Value]) search(searchStart *ListElement[Key, Value], hash uin

for {
if hash == found.keyHash && key == found.key { // key hash already exists, compare keys
return nil, found, nil
if found.deleted.Load() == 0 {
return nil, found, nil
}
}

if hash < found.keyHash { // new item needs to be inserted before the found value
Expand All @@ -116,12 +118,21 @@ func (l *List[Key, Value]) insertAt(element, left, right *ListElement[Key, Value
left = l.head
}

if left != l.head && left.deleted.Load() != 0 {
return false // left was deleted concurrently
}

element.next.Store(right)

if !left.next.CompareAndSwap(right, element) {
return false // item was modified concurrently
}

if left != l.head && left.deleted.Load() != 0 {
element.deleted.Store(1)
return false // left was deleted concurrently
}

l.count.Add(1)
return true
}