github.com/haraldrudell/parl@v0.4.176/pslices/thread-safe-slice.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pslices 7 8 import ( 9 "sync" 10 11 "golang.org/x/exp/slices" 12 ) 13 14 // ThreadSafeSlice provides certain thread-safe operations on a slice 15 // using [sync.RWMutex] mechanic 16 // 17 // thread-safety challenges with a slice: 18 // - multiple slice instances may reference the same underlying array 19 // - append operation may change address of data and create multiple arrays 20 // - data race in reading and writing slice elements 21 // - multiple threads accessing a slice instance or their underlying array 22 // - due to slicing creating new memory-sharing slices, the slice value must be 23 // fully enclosed inside the type providing thread-safety 24 // - due to copy and append operations affecting multiple memory addresses, 25 // locking is required for thread-safety 26 // - maintaining authoritative slice-state during iteration 27 // 28 // All slice operations: 29 // - len(slice) 30 // - append(slice, E...): may create new underlying array 31 // - cap(slice) 32 // - make([]E, n) 33 // - slice[:b] 34 // - slice[a:] 35 // - slice[a:b] 36 // - slice[:] 37 // - slice[a] 38 // - &slice[a]: address of slice element 39 // - &slice: address of slice 40 // - copy(slice, slice2) 41 // - slice literal 42 // - slice variable declaration 43 // - assignment of slice element 44 type ThreadSafeSlice[T any] struct { 45 lock sync.RWMutex 46 slice []T 47 } 48 49 func NewThreadSafeSlice[T any]() (threadSafeSlice *ThreadSafeSlice[T]) { 50 return &ThreadSafeSlice[T]{} 51 } 52 53 func (t *ThreadSafeSlice[T]) Append(element T) { 54 t.lock.Lock() 55 defer t.lock.Unlock() 56 57 t.slice = append(t.slice, element) 58 } 59 60 func (t *ThreadSafeSlice[T]) Get(index int) (element T, hasValue bool) { 61 t.lock.RLock() 62 defer t.lock.RUnlock() 63 64 if hasValue = index >= 0 && index < len(t.slice); !hasValue { 65 return 66 } 67 68 element = t.slice[index] 69 return 70 } 71 72 func (t *ThreadSafeSlice[T]) Put(element T, index int) (success bool) { 73 t.lock.Lock() 74 defer t.lock.Unlock() 75 76 if success = index >= 0 && index < len(t.slice); !success { 77 return 78 } 79 80 t.slice[index] = element 81 return 82 } 83 84 func (t *ThreadSafeSlice[T]) Length() (length int) { 85 t.lock.RLock() 86 defer t.lock.RUnlock() 87 88 return len(t.slice) 89 } 90 91 func (t *ThreadSafeSlice[T]) SliceClone() (clone []T) { 92 t.lock.RLock() 93 defer t.lock.RUnlock() 94 95 return slices.Clone(t.slice) 96 } 97 98 func (t *ThreadSafeSlice[T]) TrimLeft(count int) { 99 t.lock.Lock() 100 defer t.lock.Unlock() 101 102 TrimLeft(&t.slice, count) 103 } 104 105 func (t *ThreadSafeSlice[T]) SetLength(newLength int) { 106 t.lock.Lock() 107 defer t.lock.Unlock() 108 109 SetLength(&t.slice, newLength) 110 } 111 112 func (t *ThreadSafeSlice[T]) Clear() { 113 t.SetLength(0) 114 }