|
@@ -23,11 +23,18 @@ func initClosedChan() <-chan struct{} {
|
|
|
return ch
|
|
return ch
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// AtomicValue is the generic version of atomic.Value.
|
|
|
|
|
|
|
+// AtomicValue is the generic version of [atomic.Value].
|
|
|
type AtomicValue[T any] struct {
|
|
type AtomicValue[T any] struct {
|
|
|
v atomic.Value
|
|
v atomic.Value
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// wrappedValue is used to wrap a value T in a concrete type,
|
|
|
|
|
+// otherwise atomic.Value.Store may panic due to mismatching types in interfaces.
|
|
|
|
|
+// This wrapping is not necessary for non-interface kinds of T,
|
|
|
|
|
+// but there is no harm in wrapping anyways.
|
|
|
|
|
+// See https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/sync/atomic/value.go;l=78
|
|
|
|
|
+type wrappedValue[T any] struct{ v T }
|
|
|
|
|
+
|
|
|
// Load returns the value set by the most recent Store.
|
|
// Load returns the value set by the most recent Store.
|
|
|
// It returns the zero value for T if the value is empty.
|
|
// It returns the zero value for T if the value is empty.
|
|
|
func (v *AtomicValue[T]) Load() T {
|
|
func (v *AtomicValue[T]) Load() T {
|
|
@@ -40,7 +47,7 @@ func (v *AtomicValue[T]) Load() T {
|
|
|
func (v *AtomicValue[T]) LoadOk() (_ T, ok bool) {
|
|
func (v *AtomicValue[T]) LoadOk() (_ T, ok bool) {
|
|
|
x := v.v.Load()
|
|
x := v.v.Load()
|
|
|
if x != nil {
|
|
if x != nil {
|
|
|
- return x.(T), true
|
|
|
|
|
|
|
+ return x.(wrappedValue[T]).v, true
|
|
|
}
|
|
}
|
|
|
var zero T
|
|
var zero T
|
|
|
return zero, false
|
|
return zero, false
|
|
@@ -48,22 +55,22 @@ func (v *AtomicValue[T]) LoadOk() (_ T, ok bool) {
|
|
|
|
|
|
|
|
// Store sets the value of the Value to x.
|
|
// Store sets the value of the Value to x.
|
|
|
func (v *AtomicValue[T]) Store(x T) {
|
|
func (v *AtomicValue[T]) Store(x T) {
|
|
|
- v.v.Store(x)
|
|
|
|
|
|
|
+ v.v.Store(wrappedValue[T]{x})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Swap stores new into Value and returns the previous value.
|
|
// Swap stores new into Value and returns the previous value.
|
|
|
// It returns the zero value for T if the value is empty.
|
|
// It returns the zero value for T if the value is empty.
|
|
|
func (v *AtomicValue[T]) Swap(x T) (old T) {
|
|
func (v *AtomicValue[T]) Swap(x T) (old T) {
|
|
|
- oldV := v.v.Swap(x)
|
|
|
|
|
|
|
+ oldV := v.v.Swap(wrappedValue[T]{x})
|
|
|
if oldV != nil {
|
|
if oldV != nil {
|
|
|
- return oldV.(T)
|
|
|
|
|
|
|
+ return oldV.(wrappedValue[T]).v
|
|
|
}
|
|
}
|
|
|
return old
|
|
return old
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// CompareAndSwap executes the compare-and-swap operation for the Value.
|
|
// CompareAndSwap executes the compare-and-swap operation for the Value.
|
|
|
func (v *AtomicValue[T]) CompareAndSwap(oldV, newV T) (swapped bool) {
|
|
func (v *AtomicValue[T]) CompareAndSwap(oldV, newV T) (swapped bool) {
|
|
|
- return v.v.CompareAndSwap(oldV, newV)
|
|
|
|
|
|
|
+ return v.v.CompareAndSwap(wrappedValue[T]{oldV}, wrappedValue[T]{newV})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// WaitGroupChan is like a sync.WaitGroup, but has a chan that closes
|
|
// WaitGroupChan is like a sync.WaitGroup, but has a chan that closes
|