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  }