Source file src/sync/map_reference_test.go

     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(any) (any, bool)
    17  	Store(key, value any)
    18  	LoadOrStore(key, value any) (actual any, loaded bool)
    19  	LoadAndDelete(key any) (value any, loaded bool)
    20  	Delete(any)
    21  	Swap(key, value any) (previous any, loaded bool)
    22  	CompareAndSwap(key, old, new any) (swapped bool)
    23  	CompareAndDelete(key, old any) (deleted bool)
    24  	Range(func(key, value any) (shouldContinue bool))
    25  }
    26  
    27  var (
    28  	_ mapInterface = &RWMutexMap{}
    29  	_ mapInterface = &DeepCopyMap{}
    30  )
    31  
    32  // RWMutexMap is an implementation of mapInterface using a sync.RWMutex.
    33  type RWMutexMap struct {
    34  	mu    sync.RWMutex
    35  	dirty map[any]any
    36  }
    37  
    38  func (m *RWMutexMap) Load(key any) (value any, ok bool) {
    39  	m.mu.RLock()
    40  	value, ok = m.dirty[key]
    41  	m.mu.RUnlock()
    42  	return
    43  }
    44  
    45  func (m *RWMutexMap) Store(key, value any) {
    46  	m.mu.Lock()
    47  	if m.dirty == nil {
    48  		m.dirty = make(map[any]any)
    49  	}
    50  	m.dirty[key] = value
    51  	m.mu.Unlock()
    52  }
    53  
    54  func (m *RWMutexMap) LoadOrStore(key, value any) (actual any, loaded bool) {
    55  	m.mu.Lock()
    56  	actual, loaded = m.dirty[key]
    57  	if !loaded {
    58  		actual = value
    59  		if m.dirty == nil {
    60  			m.dirty = make(map[any]any)
    61  		}
    62  		m.dirty[key] = value
    63  	}
    64  	m.mu.Unlock()
    65  	return actual, loaded
    66  }
    67  
    68  func (m *RWMutexMap) Swap(key, value any) (previous any, loaded bool) {
    69  	m.mu.Lock()
    70  	if m.dirty == nil {
    71  		m.dirty = make(map[any]any)
    72  	}
    73  
    74  	previous, loaded = m.dirty[key]
    75  	m.dirty[key] = value
    76  	m.mu.Unlock()
    77  	return
    78  }
    79  
    80  func (m *RWMutexMap) LoadAndDelete(key any) (value any, loaded bool) {
    81  	m.mu.Lock()
    82  	value, loaded = m.dirty[key]
    83  	if !loaded {
    84  		m.mu.Unlock()
    85  		return nil, false
    86  	}
    87  	delete(m.dirty, key)
    88  	m.mu.Unlock()
    89  	return value, loaded
    90  }
    91  
    92  func (m *RWMutexMap) Delete(key any) {
    93  	m.mu.Lock()
    94  	delete(m.dirty, key)
    95  	m.mu.Unlock()
    96  }
    97  
    98  func (m *RWMutexMap) CompareAndSwap(key, old, new any) (swapped bool) {
    99  	m.mu.Lock()
   100  	defer m.mu.Unlock()
   101  	if m.dirty == nil {
   102  		return false
   103  	}
   104  
   105  	value, loaded := m.dirty[key]
   106  	if loaded && value == old {
   107  		m.dirty[key] = new
   108  		return true
   109  	}
   110  	return false
   111  }
   112  
   113  func (m *RWMutexMap) CompareAndDelete(key, old any) (deleted bool) {
   114  	m.mu.Lock()
   115  	defer m.mu.Unlock()
   116  	if m.dirty == nil {
   117  		return false
   118  	}
   119  
   120  	value, loaded := m.dirty[key]
   121  	if loaded && value == old {
   122  		delete(m.dirty, key)
   123  		return true
   124  	}
   125  	return false
   126  }
   127  
   128  func (m *RWMutexMap) Range(f func(key, value any) (shouldContinue bool)) {
   129  	m.mu.RLock()
   130  	keys := make([]any, 0, len(m.dirty))
   131  	for k := range m.dirty {
   132  		keys = append(keys, k)
   133  	}
   134  	m.mu.RUnlock()
   135  
   136  	for _, k := range keys {
   137  		v, ok := m.Load(k)
   138  		if !ok {
   139  			continue
   140  		}
   141  		if !f(k, v) {
   142  			break
   143  		}
   144  	}
   145  }
   146  
   147  // DeepCopyMap is an implementation of mapInterface using a Mutex and
   148  // atomic.Value.  It makes deep copies of the map on every write to avoid
   149  // acquiring the Mutex in Load.
   150  type DeepCopyMap struct {
   151  	mu    sync.Mutex
   152  	clean atomic.Value
   153  }
   154  
   155  func (m *DeepCopyMap) Load(key any) (value any, ok bool) {
   156  	clean, _ := m.clean.Load().(map[any]any)
   157  	value, ok = clean[key]
   158  	return value, ok
   159  }
   160  
   161  func (m *DeepCopyMap) Store(key, value any) {
   162  	m.mu.Lock()
   163  	dirty := m.dirty()
   164  	dirty[key] = value
   165  	m.clean.Store(dirty)
   166  	m.mu.Unlock()
   167  }
   168  
   169  func (m *DeepCopyMap) LoadOrStore(key, value any) (actual any, loaded bool) {
   170  	clean, _ := m.clean.Load().(map[any]any)
   171  	actual, loaded = clean[key]
   172  	if loaded {
   173  		return actual, loaded
   174  	}
   175  
   176  	m.mu.Lock()
   177  	// Reload clean in case it changed while we were waiting on m.mu.
   178  	clean, _ = m.clean.Load().(map[any]any)
   179  	actual, loaded = clean[key]
   180  	if !loaded {
   181  		dirty := m.dirty()
   182  		dirty[key] = value
   183  		actual = value
   184  		m.clean.Store(dirty)
   185  	}
   186  	m.mu.Unlock()
   187  	return actual, loaded
   188  }
   189  
   190  func (m *DeepCopyMap) Swap(key, value any) (previous any, loaded bool) {
   191  	m.mu.Lock()
   192  	dirty := m.dirty()
   193  	previous, loaded = dirty[key]
   194  	dirty[key] = value
   195  	m.clean.Store(dirty)
   196  	m.mu.Unlock()
   197  	return
   198  }
   199  
   200  func (m *DeepCopyMap) LoadAndDelete(key any) (value any, loaded bool) {
   201  	m.mu.Lock()
   202  	dirty := m.dirty()
   203  	value, loaded = dirty[key]
   204  	delete(dirty, key)
   205  	m.clean.Store(dirty)
   206  	m.mu.Unlock()
   207  	return
   208  }
   209  
   210  func (m *DeepCopyMap) Delete(key any) {
   211  	m.mu.Lock()
   212  	dirty := m.dirty()
   213  	delete(dirty, key)
   214  	m.clean.Store(dirty)
   215  	m.mu.Unlock()
   216  }
   217  
   218  func (m *DeepCopyMap) CompareAndSwap(key, old, new any) (swapped bool) {
   219  	clean, _ := m.clean.Load().(map[any]any)
   220  	if previous, ok := clean[key]; !ok || previous != old {
   221  		return false
   222  	}
   223  
   224  	m.mu.Lock()
   225  	defer m.mu.Unlock()
   226  	dirty := m.dirty()
   227  	value, loaded := dirty[key]
   228  	if loaded && value == old {
   229  		dirty[key] = new
   230  		m.clean.Store(dirty)
   231  		return true
   232  	}
   233  	return false
   234  }
   235  
   236  func (m *DeepCopyMap) CompareAndDelete(key, old any) (deleted bool) {
   237  	clean, _ := m.clean.Load().(map[any]any)
   238  	if previous, ok := clean[key]; !ok || previous != old {
   239  		return false
   240  	}
   241  
   242  	m.mu.Lock()
   243  	defer m.mu.Unlock()
   244  
   245  	dirty := m.dirty()
   246  	value, loaded := dirty[key]
   247  	if loaded && value == old {
   248  		delete(dirty, key)
   249  		m.clean.Store(dirty)
   250  		return true
   251  	}
   252  	return false
   253  }
   254  
   255  func (m *DeepCopyMap) Range(f func(key, value any) (shouldContinue bool)) {
   256  	clean, _ := m.clean.Load().(map[any]any)
   257  	for k, v := range clean {
   258  		if !f(k, v) {
   259  			break
   260  		}
   261  	}
   262  }
   263  
   264  func (m *DeepCopyMap) dirty() map[any]any {
   265  	clean, _ := m.clean.Load().(map[any]any)
   266  	dirty := make(map[any]any, len(clean)+1)
   267  	for k, v := range clean {
   268  		dirty[k] = v
   269  	}
   270  	return dirty
   271  }
   272  

View as plain text