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  }