github.com/haraldrudell/parl@v0.4.176/pmaps/rwmap.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 // Package pmaps provides an unordered, thread-safe, RWMutex-mechanic map pmaps.RWMap. 7 package pmaps 8 9 import ( 10 "github.com/haraldrudell/parl/parli" 11 "github.com/haraldrudell/parl/pmaps/pmaps2" 12 ) 13 14 // RWMap is a thread-safe mapping based on read/write mutex 15 // - 5 native Go map functions: Get Put Delete Length Range 16 // - — Delete optionally writes zero-value 17 // - complex atomic methods: 18 // - — GetOrCreate method is an atomic, thread-safe operation 19 // as opposed to Get-then-Put 20 // - — PutIf is atomic, thread-safe operation 21 // - convenience methods: 22 // - — Clone Clone2 based on [maps.Clone] 23 // - — Clear using fast recreate or [maps.Range] optionally writing zero-values 24 // - order functions: 25 // - — List unordered values 26 // - — Keys unordered keys 27 // - V is copied so if V is large or contains locks, use pointer to V type 28 // - RWMap implements [parli.ThreadSafeMap][K comparable, V any] 29 // - — 30 // - map mechanic is Go map 31 // - RWMap uses reader/writer mutual exclusion lock for slightly higher performance 32 // - Get methods are O(1) 33 // - innermost type provides thread-safety 34 // - outermost type provides map api 35 type RWMap[K comparable, V any] struct { 36 // Get() GetOrCreate() Put() Delete() Length() Range() Clear() List() 37 threadSafeMap[K, V] 38 } 39 40 // NewRWMap returns a thread-safe map implementation 41 func NewRWMap[K comparable, V any]() (rwMap parli.ThreadSafeMap[K, V]) { 42 return NewRWMap2[K, V]() 43 } 44 45 // NewRWMap2 returns a thread-safe map implementation 46 func NewRWMap2[K comparable, V any]() (rwMap *RWMap[K, V]) { 47 return &RWMap[K, V]{threadSafeMap: *newThreadSafeMap[K, V]()} 48 } 49 50 // Putif is conditional Put depending on the return value from the putIf function 51 // - if key does not exist in the map, the put is carried out and wasNewKey is true 52 // - if key exists and putIf is nil or returns true, the put is carried out and wasNewKey is false 53 // - if key exists and putIf returns false, the put is not carried out and wasNewKey is false 54 // - during PutIf, the map cannot be accessed and the map’s write-lock is held 55 // - PutIf is an atomic, thread-safe operation 56 func (m *RWMap[K, V]) PutIf(key K, value V, putIf func(value V) (doPut bool)) (wasNewKey bool) { 57 defer m.tsm.Lock()() 58 59 existing, keyExists := m.tsm.Get(key) 60 wasNewKey = !keyExists 61 if keyExists && putIf != nil && !putIf(existing) { 62 return // putIf false return: this value should not be updated 63 } 64 m.tsm.Put(key, value) 65 66 return 67 } 68 69 // Clone returns a shallow clone of the map 70 func (m *RWMap[K, V]) Clone() (clone parli.ThreadSafeMap[K, V]) { 71 return m.Clone2() 72 } 73 74 // Clone returns a shallow clone of the map 75 func (m *RWMap[K, V]) Clone2() (clone *RWMap[K, V]) { 76 defer m.tsm.RLock()() 77 78 return &RWMap[K, V]{threadSafeMap: *m.threadSafeMap.clone()} 79 } 80 81 // Keys provides the mapping keys, undefined ordering 82 // - O(n) 83 // - invoked while holding RLock or Lock 84 func (m *RWMap[K, V]) Keys(n ...int) (keys []K) { 85 // get n 86 var n0 int 87 if len(n) > 0 { 88 n0 = n[0] 89 } 90 defer m.tsm.RLock()() 91 92 // n0 is actual length to use 93 var length = m.tsm.Length() 94 if n0 <= 0 { 95 n0 = length 96 } else if n0 > length { 97 n0 = length 98 } 99 100 var keyRanger = pmaps2.KeyRanger[K, V]{List: make([]K, n0)} 101 m.tsm.Range(keyRanger.RangeFunc) 102 keys = keyRanger.List 103 104 return 105 }