-
Notifications
You must be signed in to change notification settings - Fork 11
/
map_concurrent.go
147 lines (131 loc) · 3.94 KB
/
map_concurrent.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package async
import (
"sync"
"sync/atomic"
"time"
)
// ConcurrentMap implements the async.Map interface in a thread-safe manner
// by delegating load/store operations to the underlying sync.Map.
// A ConcurrentMap must not be copied.
//
// The sync.Map type is optimized for two common use cases: (1) when the entry for a given
// key is only ever written once but read many times, as in caches that only grow,
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
// sets of keys. In these two cases, use of a sync.Map may significantly reduce lock
// contention compared to a Go map paired with a separate sync.Mutex or sync.RWMutex.
type ConcurrentMap[K comparable, V any] struct {
m *atomic.Pointer[sync.Map]
size atomic.Int64
clearing atomic.Bool
}
var _ Map[int, any] = (*ConcurrentMap[int, any])(nil)
// NewConcurrentMap returns a new ConcurrentMap instance.
func NewConcurrentMap[K comparable, V any]() *ConcurrentMap[K, V] {
var underlying atomic.Pointer[sync.Map]
underlying.Store(&sync.Map{})
return &ConcurrentMap[K, V]{
m: &underlying,
}
}
// Clear removes all of the mappings from this map.
func (cm *ConcurrentMap[K, V]) Clear() {
cm.clearing.Store(true)
defer cm.clearing.Store(false)
cm.m.Store(&sync.Map{})
cm.size.Store(0)
}
// ComputeIfAbsent attempts to compute a value using the given mapping
// function and enters it into the map, if the specified key is not
// already associated with a value.
func (cm *ConcurrentMap[K, V]) ComputeIfAbsent(key K, mappingFunction func(K) *V) *V {
value := cm.Get(key)
if value == nil {
computed, loaded := cm.smap().LoadOrStore(key, mappingFunction(key))
if !loaded {
cm.size.Add(1)
}
return computed.(*V)
}
return value
}
// ContainsKey returns true if this map contains a mapping for the
// specified key.
func (cm *ConcurrentMap[K, V]) ContainsKey(key K) bool {
return cm.Get(key) != nil
}
// Get returns the value to which the specified key is mapped, or nil if
// this map contains no mapping for the key.
func (cm *ConcurrentMap[K, V]) Get(key K) *V {
value, ok := cm.smap().Load(key)
if !ok {
return nil
}
return value.(*V)
}
// GetOrDefault returns the value to which the specified key is mapped, or
// defaultValue if this map contains no mapping for the key.
func (cm *ConcurrentMap[K, V]) GetOrDefault(key K, defaultValue *V) *V {
value, ok := cm.smap().Load(key)
if !ok {
return defaultValue
}
return value.(*V)
}
// IsEmpty returns true if this map contains no key-value mappings.
func (cm *ConcurrentMap[K, V]) IsEmpty() bool {
return cm.Size() == 0
}
// KeySet returns a slice of the keys contained in this map.
func (cm *ConcurrentMap[K, V]) KeySet() []K {
keys := make([]K, 0, cm.Size())
rangeKeysFunc := func(key any, _ any) bool {
keys = append(keys, key.(K))
return true
}
cm.smap().Range(rangeKeysFunc)
return keys
}
// Put associates the specified value with the specified key in this map.
func (cm *ConcurrentMap[K, V]) Put(key K, value *V) {
_, loaded := cm.smap().Swap(key, value)
if !loaded {
cm.size.Add(1)
}
}
// Remove removes the mapping for a key from this map if it is present,
// returning the previous value or nil if none.
func (cm *ConcurrentMap[K, V]) Remove(key K) *V {
value, loaded := cm.smap().LoadAndDelete(key)
if !loaded {
return nil
}
cm.size.Add(-1)
return value.(*V)
}
// Size returns the number of key-value mappings in this map.
func (cm *ConcurrentMap[K, V]) Size() int {
size := cm.size.Load()
if size > 0 {
return int(size)
}
return 0
}
// Values returns a slice of the values contained in this map.
func (cm *ConcurrentMap[K, V]) Values() []*V {
values := make([]*V, 0, cm.Size())
rangeValuesFunc := func(_ any, value any) bool {
values = append(values, value.(*V))
return true
}
cm.smap().Range(rangeValuesFunc)
return values
}
func (cm *ConcurrentMap[K, V]) smap() *sync.Map {
for {
if !cm.clearing.Load() {
break
}
time.Sleep(time.Nanosecond)
}
return cm.m.Load()
}