From 03b3716d65907388a22dab7cd0d5281c36effaa3 Mon Sep 17 00:00:00 2001 From: reugn Date: Fri, 27 Oct 2023 10:05:38 +0300 Subject: [PATCH] refactor: upgrade to go1.20 in ConcurrentMap --- concurrent_map.go | 35 +++++++++++++++-------------------- no_copy.go | 14 -------------- 2 files changed, 15 insertions(+), 34 deletions(-) delete mode 100644 no_copy.go diff --git a/concurrent_map.go b/concurrent_map.go index 68dd6f1..3c22f50 100644 --- a/concurrent_map.go +++ b/concurrent_map.go @@ -16,17 +16,16 @@ import ( // 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.Value // TODO: use atomic.Pointer when upgrading to/past go1.19 - size int64 - clearing int32 // TODO: use atomic.Bool when upgrading to/past go1.19 - _ noCopy + 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.Value + var underlying atomic.Pointer[sync.Map] underlying.Store(&sync.Map{}) return &ConcurrentMap[K, V]{ m: &underlying, @@ -35,10 +34,10 @@ func NewConcurrentMap[K comparable, V any]() *ConcurrentMap[K, V] { // Clear removes all of the mappings from this map. func (cm *ConcurrentMap[K, V]) Clear() { - atomic.StoreInt32(&cm.clearing, 1) - defer atomic.StoreInt32(&cm.clearing, 0) - _ = cm.m.Swap(&sync.Map{}) - atomic.StoreInt64(&cm.size, 0) + 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 @@ -49,7 +48,7 @@ func (cm *ConcurrentMap[K, V]) ComputeIfAbsent(key K, mappingFunction func(K) *V if value == nil { computed, loaded := cm.smap().LoadOrStore(key, mappingFunction(key)) if !loaded { - atomic.AddInt64(&cm.size, 1) + cm.size.Add(1) } return computed.(*V) } @@ -100,12 +99,9 @@ func (cm *ConcurrentMap[K, V]) KeySet() []K { // Put associates the specified value with the specified key in this map. func (cm *ConcurrentMap[K, V]) Put(key K, value *V) { - // TODO: use sync.Map.Swap when upgrading to/past go1.20 - _, loaded := cm.smap().LoadOrStore(key, value) + _, loaded := cm.smap().Swap(key, value) if !loaded { - atomic.AddInt64(&cm.size, 1) - } else { - cm.smap().Store(key, value) + cm.size.Add(1) } } @@ -116,13 +112,13 @@ func (cm *ConcurrentMap[K, V]) Remove(key K) *V { if !loaded { return nil } - atomic.AddInt64(&cm.size, -1) + 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 := atomic.LoadInt64(&cm.size) + size := cm.size.Load() if size > 0 { return int(size) } @@ -142,11 +138,10 @@ func (cm *ConcurrentMap[K, V]) Values() []*V { func (cm *ConcurrentMap[K, V]) smap() *sync.Map { for { - c := atomic.LoadInt32(&cm.clearing) - if c == 0 { + if !cm.clearing.Load() { break } time.Sleep(time.Nanosecond) } - return cm.m.Load().(*sync.Map) + return cm.m.Load() } diff --git a/no_copy.go b/no_copy.go deleted file mode 100644 index 539056f..0000000 --- a/no_copy.go +++ /dev/null @@ -1,14 +0,0 @@ -package async - -// noCopy may be added to structs which must not be copied -// after the first use. -// -// See https://golang.org/issues/8005#issuecomment-190753527 -// for details. -// -// Note that it must not be embedded, due to the Lock and Unlock methods. -type noCopy struct{} - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} -func (*noCopy) Unlock() {}