Black Lives Matter. Support the Equal Justice Initiative.

Source file src/sync/map_reference_test.go

Documentation: sync

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sync_test
     6  
     7  import (
     8  	"sync"
     9  	"sync/atomic"
    10  )
    11  
    12  // This file contains reference map implementations for unit-tests.
    13  
    14  // mapInterface is the interface Map implements.
    15  type mapInterface interface {
    16  	Load(interface{}) (interface{}, bool)
    17  	Store(key, value interface{})
    18  	LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
    19  	LoadAndDelete(key interface{}) (value interface{}, loaded bool)
    20  	Delete(interface{})
    21  	Range(func(key, value interface{}) (shouldContinue bool))
    22  }
    23  
    24  // RWMutexMap is an implementation of mapInterface using a sync.RWMutex.
    25  type RWMutexMap struct {
    26  	mu    sync.RWMutex
    27  	dirty map[interface{}]interface{}
    28  }
    29  
    30  func (m *RWMutexMap) Load(key interface{}) (value interface{}, ok bool) {
    31  	m.mu.RLock()
    32  	value, ok = m.dirty[key]
    33  	m.mu.RUnlock()
    34  	return
    35  }
    36  
    37  func (m *RWMutexMap) Store(key, value interface{}) {
    38  	m.mu.Lock()
    39  	if m.dirty == nil {
    40  		m.dirty = make(map[interface{}]interface{})
    41  	}
    42  	m.dirty[key] = value
    43  	m.mu.Unlock()
    44  }
    45  
    46  func (m *RWMutexMap) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
    47  	m.mu.Lock()
    48  	actual, loaded = m.dirty[key]
    49  	if !loaded {
    50  		actual = value
    51  		if m.dirty == nil {
    52  			m.dirty = make(map[interface{}]interface{})
    53  		}
    54  		m.dirty[key] = value
    55  	}
    56  	m.mu.Unlock()
    57  	return actual, loaded
    58  }
    59  
    60  func (m *RWMutexMap) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
    61  	m.mu.Lock()
    62  	value, loaded = m.dirty[key]
    63  	if !loaded {
    64  		m.mu.Unlock()
    65  		return nil, false
    66  	}
    67  	delete(m.dirty, key)
    68  	m.mu.Unlock()
    69  	return value, loaded
    70  }
    71  
    72  func (m *RWMutexMap) Delete(key interface{}) {
    73  	m.mu.Lock()
    74  	delete(m.dirty, key)
    75  	m.mu.Unlock()
    76  }
    77  
    78  func (m *RWMutexMap) Range(f func(key, value interface{}) (shouldContinue bool)) {
    79  	m.mu.RLock()
    80  	keys := make([]interface{}, 0, len(m.dirty))
    81  	for k := range m.dirty {
    82  		keys = append(keys, k)
    83  	}
    84  	m.mu.RUnlock()
    85  
    86  	for _, k := range keys {
    87  		v, ok := m.Load(k)
    88  		if !ok {
    89  			continue
    90  		}
    91  		if !f(k, v) {
    92  			break
    93  		}
    94  	}
    95  }
    96  
    97  // DeepCopyMap is an implementation of mapInterface using a Mutex and
    98  // atomic.Value.  It makes deep copies of the map on every write to avoid
    99  // acquiring the Mutex in Load.
   100  type DeepCopyMap struct {
   101  	mu    sync.Mutex
   102  	clean atomic.Value
   103  }
   104  
   105  func (m *DeepCopyMap) Load(key interface{}) (value interface{}, ok bool) {
   106  	clean, _ := m.clean.Load().(map[interface{}]interface{})
   107  	value, ok = clean[key]
   108  	return value, ok
   109  }
   110  
   111  func (m *DeepCopyMap) Store(key, value interface{}) {
   112  	m.mu.Lock()
   113  	dirty := m.dirty()
   114  	dirty[key] = value
   115  	m.clean.Store(dirty)
   116  	m.mu.Unlock()
   117  }
   118  
   119  func (m *DeepCopyMap) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
   120  	clean, _ := m.clean.Load().(map[interface{}]interface{})
   121  	actual, loaded = clean[key]
   122  	if loaded {
   123  		return actual, loaded
   124  	}
   125  
   126  	m.mu.Lock()
   127  	// Reload clean in case it changed while we were waiting on m.mu.
   128  	clean, _ = m.clean.Load().(map[interface{}]interface{})
   129  	actual, loaded = clean[key]
   130  	if !loaded {
   131  		dirty := m.dirty()
   132  		dirty[key] = value
   133  		actual = value
   134  		m.clean.Store(dirty)
   135  	}
   136  	m.mu.Unlock()
   137  	return actual, loaded
   138  }
   139  
   140  func (m *DeepCopyMap) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
   141  	m.mu.Lock()
   142  	dirty := m.dirty()
   143  	value, loaded = dirty[key]
   144  	delete(dirty, key)
   145  	m.clean.Store(dirty)
   146  	m.mu.Unlock()
   147  	return
   148  }
   149  
   150  func (m *DeepCopyMap) Delete(key interface{}) {
   151  	m.mu.Lock()
   152  	dirty := m.dirty()
   153  	delete(dirty, key)
   154  	m.clean.Store(dirty)
   155  	m.mu.Unlock()
   156  }
   157  
   158  func (m *DeepCopyMap) Range(f func(key, value interface{}) (shouldContinue bool)) {
   159  	clean, _ := m.clean.Load().(map[interface{}]interface{})
   160  	for k, v := range clean {
   161  		if !f(k, v) {
   162  			break
   163  		}
   164  	}
   165  }
   166  
   167  func (m *DeepCopyMap) dirty() map[interface{}]interface{} {
   168  	clean, _ := m.clean.Load().(map[interface{}]interface{})
   169  	dirty := make(map[interface{}]interface{}, len(clean)+1)
   170  	for k, v := range clean {
   171  		dirty[k] = v
   172  	}
   173  	return dirty
   174  }
   175  

View as plain text