summaryrefslogtreecommitdiffstats
path: root/src/sync/map_reference_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/sync/map_reference_test.go')
-rw-r--r--src/sync/map_reference_test.go271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/sync/map_reference_test.go b/src/sync/map_reference_test.go
new file mode 100644
index 0000000..aa5ebf3
--- /dev/null
+++ b/src/sync/map_reference_test.go
@@ -0,0 +1,271 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sync_test
+
+import (
+ "sync"
+ "sync/atomic"
+)
+
+// This file contains reference map implementations for unit-tests.
+
+// mapInterface is the interface Map implements.
+type mapInterface interface {
+ Load(any) (any, bool)
+ Store(key, value any)
+ LoadOrStore(key, value any) (actual any, loaded bool)
+ LoadAndDelete(key any) (value any, loaded bool)
+ Delete(any)
+ Swap(key, value any) (previous any, loaded bool)
+ CompareAndSwap(key, old, new any) (swapped bool)
+ CompareAndDelete(key, old any) (deleted bool)
+ Range(func(key, value any) (shouldContinue bool))
+}
+
+var (
+ _ mapInterface = &RWMutexMap{}
+ _ mapInterface = &DeepCopyMap{}
+)
+
+// RWMutexMap is an implementation of mapInterface using a sync.RWMutex.
+type RWMutexMap struct {
+ mu sync.RWMutex
+ dirty map[any]any
+}
+
+func (m *RWMutexMap) Load(key any) (value any, ok bool) {
+ m.mu.RLock()
+ value, ok = m.dirty[key]
+ m.mu.RUnlock()
+ return
+}
+
+func (m *RWMutexMap) Store(key, value any) {
+ m.mu.Lock()
+ if m.dirty == nil {
+ m.dirty = make(map[any]any)
+ }
+ m.dirty[key] = value
+ m.mu.Unlock()
+}
+
+func (m *RWMutexMap) LoadOrStore(key, value any) (actual any, loaded bool) {
+ m.mu.Lock()
+ actual, loaded = m.dirty[key]
+ if !loaded {
+ actual = value
+ if m.dirty == nil {
+ m.dirty = make(map[any]any)
+ }
+ m.dirty[key] = value
+ }
+ m.mu.Unlock()
+ return actual, loaded
+}
+
+func (m *RWMutexMap) Swap(key, value any) (previous any, loaded bool) {
+ m.mu.Lock()
+ if m.dirty == nil {
+ m.dirty = make(map[any]any)
+ }
+
+ previous, loaded = m.dirty[key]
+ m.dirty[key] = value
+ m.mu.Unlock()
+ return
+}
+
+func (m *RWMutexMap) LoadAndDelete(key any) (value any, loaded bool) {
+ m.mu.Lock()
+ value, loaded = m.dirty[key]
+ if !loaded {
+ m.mu.Unlock()
+ return nil, false
+ }
+ delete(m.dirty, key)
+ m.mu.Unlock()
+ return value, loaded
+}
+
+func (m *RWMutexMap) Delete(key any) {
+ m.mu.Lock()
+ delete(m.dirty, key)
+ m.mu.Unlock()
+}
+
+func (m *RWMutexMap) CompareAndSwap(key, old, new any) (swapped bool) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.dirty == nil {
+ return false
+ }
+
+ value, loaded := m.dirty[key]
+ if loaded && value == old {
+ m.dirty[key] = new
+ return true
+ }
+ return false
+}
+
+func (m *RWMutexMap) CompareAndDelete(key, old any) (deleted bool) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.dirty == nil {
+ return false
+ }
+
+ value, loaded := m.dirty[key]
+ if loaded && value == old {
+ delete(m.dirty, key)
+ return true
+ }
+ return false
+}
+
+func (m *RWMutexMap) Range(f func(key, value any) (shouldContinue bool)) {
+ m.mu.RLock()
+ keys := make([]any, 0, len(m.dirty))
+ for k := range m.dirty {
+ keys = append(keys, k)
+ }
+ m.mu.RUnlock()
+
+ for _, k := range keys {
+ v, ok := m.Load(k)
+ if !ok {
+ continue
+ }
+ if !f(k, v) {
+ break
+ }
+ }
+}
+
+// DeepCopyMap is an implementation of mapInterface using a Mutex and
+// atomic.Value. It makes deep copies of the map on every write to avoid
+// acquiring the Mutex in Load.
+type DeepCopyMap struct {
+ mu sync.Mutex
+ clean atomic.Value
+}
+
+func (m *DeepCopyMap) Load(key any) (value any, ok bool) {
+ clean, _ := m.clean.Load().(map[any]any)
+ value, ok = clean[key]
+ return value, ok
+}
+
+func (m *DeepCopyMap) Store(key, value any) {
+ m.mu.Lock()
+ dirty := m.dirty()
+ dirty[key] = value
+ m.clean.Store(dirty)
+ m.mu.Unlock()
+}
+
+func (m *DeepCopyMap) LoadOrStore(key, value any) (actual any, loaded bool) {
+ clean, _ := m.clean.Load().(map[any]any)
+ actual, loaded = clean[key]
+ if loaded {
+ return actual, loaded
+ }
+
+ m.mu.Lock()
+ // Reload clean in case it changed while we were waiting on m.mu.
+ clean, _ = m.clean.Load().(map[any]any)
+ actual, loaded = clean[key]
+ if !loaded {
+ dirty := m.dirty()
+ dirty[key] = value
+ actual = value
+ m.clean.Store(dirty)
+ }
+ m.mu.Unlock()
+ return actual, loaded
+}
+
+func (m *DeepCopyMap) Swap(key, value any) (previous any, loaded bool) {
+ m.mu.Lock()
+ dirty := m.dirty()
+ previous, loaded = dirty[key]
+ dirty[key] = value
+ m.clean.Store(dirty)
+ m.mu.Unlock()
+ return
+}
+
+func (m *DeepCopyMap) LoadAndDelete(key any) (value any, loaded bool) {
+ m.mu.Lock()
+ dirty := m.dirty()
+ value, loaded = dirty[key]
+ delete(dirty, key)
+ m.clean.Store(dirty)
+ m.mu.Unlock()
+ return
+}
+
+func (m *DeepCopyMap) Delete(key any) {
+ m.mu.Lock()
+ dirty := m.dirty()
+ delete(dirty, key)
+ m.clean.Store(dirty)
+ m.mu.Unlock()
+}
+
+func (m *DeepCopyMap) CompareAndSwap(key, old, new any) (swapped bool) {
+ clean, _ := m.clean.Load().(map[any]any)
+ if previous, ok := clean[key]; !ok || previous != old {
+ return false
+ }
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ dirty := m.dirty()
+ value, loaded := dirty[key]
+ if loaded && value == old {
+ dirty[key] = new
+ m.clean.Store(dirty)
+ return true
+ }
+ return false
+}
+
+func (m *DeepCopyMap) CompareAndDelete(key, old any) (deleted bool) {
+ clean, _ := m.clean.Load().(map[any]any)
+ if previous, ok := clean[key]; !ok || previous != old {
+ return false
+ }
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ dirty := m.dirty()
+ value, loaded := dirty[key]
+ if loaded && value == old {
+ delete(dirty, key)
+ m.clean.Store(dirty)
+ return true
+ }
+ return false
+}
+
+func (m *DeepCopyMap) Range(f func(key, value any) (shouldContinue bool)) {
+ clean, _ := m.clean.Load().(map[any]any)
+ for k, v := range clean {
+ if !f(k, v) {
+ break
+ }
+ }
+}
+
+func (m *DeepCopyMap) dirty() map[any]any {
+ clean, _ := m.clean.Load().(map[any]any)
+ dirty := make(map[any]any, len(clean)+1)
+ for k, v := range clean {
+ dirty[k] = v
+ }
+ return dirty
+}