Skip to content

Commit ba8662a

Browse files
committed
Implement Update method
The update method allows atomically mutating a value in the map. If the key does not exist, the zero value for the value type is mutated and saved.
1 parent 85296bc commit ba8662a

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

concurrent_map.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ func (m ConcurrentMap[K, V]) Set(key K, value V) {
7575
shard.Unlock()
7676
}
7777

78+
// Callback to update an element in the map.
79+
// If the element doesn't exist in the map, the parameter will receive the zero value for the value type.
80+
// The returned value will be stored in the map replacing the existing value.
81+
// It is called while lock is held, therefore it MUST NOT
82+
// try to access other keys in same map, as it can lead to deadlock since
83+
// Go sync.RWLock is not reentrant
84+
type UpdateCb[V any] func(valueInMap V) V
85+
86+
// Update an existing element using UpdateCb, assuming the key exists.
87+
// If it does not, the zero value for the value type is passed to the callback.
88+
func (m ConcurrentMap[K, V]) Update(key K, cb UpdateCb[V]) (res V) {
89+
shard := m.GetShard(key)
90+
shard.Lock()
91+
v := shard.items[key]
92+
shard.items[key] = cb(v)
93+
shard.Unlock()
94+
return res
95+
}
96+
7897
// Callback to return new element to be inserted into the map
7998
// It is called while lock is held, therefore it MUST NOT
8099
// try to access other keys in same map, as it can lead to deadlock since

concurrent_map_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,34 @@ func TestFnv32(t *testing.T) {
479479

480480
}
481481

482+
func TestUpdate(t *testing.T) {
483+
m := New[Animal]()
484+
lion := Animal{"lion"}
485+
486+
m.Set("safari", lion)
487+
m.Update("safari", func(valueInMap Animal) Animal {
488+
valueInMap.name = "tiger"
489+
return valueInMap
490+
})
491+
safari, ok := m.Get("safari")
492+
if safari.name != "tiger" || !ok {
493+
t.Error("Set, then Update failed")
494+
}
495+
496+
m.Update("marine", func(valueInMap Animal) Animal {
497+
if valueInMap.name != "" {
498+
t.Error("Update did not receive zero value for non existing key")
499+
}
500+
valueInMap.name = "whale"
501+
return valueInMap
502+
})
503+
marineAnimals, ok := m.Get("marine")
504+
if marineAnimals.name != "whale" || !ok {
505+
t.Error("Update on non-existing key failed")
506+
}
507+
508+
}
509+
482510
func TestUpsert(t *testing.T) {
483511
dolphin := Animal{"dolphin"}
484512
whale := Animal{"whale"}

0 commit comments

Comments
 (0)