| // 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(interface{}) (interface{}, bool) | |
| Store(key, value interface{}) | |
| LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) | |
| Delete(interface{}) | |
| Range(func(key, value interface{}) (shouldContinue bool)) | |
| } | |
| // RWMutexMap is an implementation of mapInterface using a sync.RWMutex. | |
| type RWMutexMap struct { | |
| mu sync.RWMutex | |
| dirty map[interface{}]interface{} | |
| } | |
| func (m *RWMutexMap) Load(key interface{}) (value interface{}, ok bool) { | |
| m.mu.RLock() | |
| value, ok = m.dirty[key] | |
| m.mu.RUnlock() | |
| return | |
| } | |
| func (m *RWMutexMap) Store(key, value interface{}) { | |
| m.mu.Lock() | |
| if m.dirty == nil { | |
| m.dirty = make(map[interface{}]interface{}) | |
| } | |
| m.dirty[key] = value | |
| m.mu.Unlock() | |
| } | |
| func (m *RWMutexMap) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { | |
| m.mu.Lock() | |
| actual, loaded = m.dirty[key] | |
| if !loaded { | |
| actual = value | |
| if m.dirty == nil { | |
| m.dirty = make(map[interface{}]interface{}) | |
| } | |
| m.dirty[key] = value | |
| } | |
| m.mu.Unlock() | |
| return actual, loaded | |
| } | |
| func (m *RWMutexMap) Delete(key interface{}) { | |
| m.mu.Lock() | |
| delete(m.dirty, key) | |
| m.mu.Unlock() | |
| } | |
| func (m *RWMutexMap) Range(f func(key, value interface{}) (shouldContinue bool)) { | |
| m.mu.RLock() | |
| keys := make([]interface{}, 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 interface{}) (value interface{}, ok bool) { | |
| clean, _ := m.clean.Load().(map[interface{}]interface{}) | |
| value, ok = clean[key] | |
| return value, ok | |
| } | |
| func (m *DeepCopyMap) Store(key, value interface{}) { | |
| m.mu.Lock() | |
| dirty := m.dirty() | |
| dirty[key] = value | |
| m.clean.Store(dirty) | |
| m.mu.Unlock() | |
| } | |
| func (m *DeepCopyMap) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { | |
| clean, _ := m.clean.Load().(map[interface{}]interface{}) | |
| 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[interface{}]interface{}) | |
| 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) Delete(key interface{}) { | |
| m.mu.Lock() | |
| dirty := m.dirty() | |
| delete(dirty, key) | |
| m.clean.Store(dirty) | |
| m.mu.Unlock() | |
| } | |
| func (m *DeepCopyMap) Range(f func(key, value interface{}) (shouldContinue bool)) { | |
| clean, _ := m.clean.Load().(map[interface{}]interface{}) | |
| for k, v := range clean { | |
| if !f(k, v) { | |
| break | |
| } | |
| } | |
| } | |
| func (m *DeepCopyMap) dirty() map[interface{}]interface{} { | |
| clean, _ := m.clean.Load().(map[interface{}]interface{}) | |
| dirty := make(map[interface{}]interface{}, len(clean)+1) | |
| for k, v := range clean { | |
| dirty[k] = v | |
| } | |
| return dirty | |
| } |