atomic.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Package atomic contains type-safe atomic types.
  2. //
  3. // The zero value for the numeric types cannot be used. Use New*. The
  4. // rationale for this behaviour is that copying an atomic integer is not
  5. // reliable. Copying can be prevented by embedding sync.Mutex, but that bloats
  6. // the type.
  7. package atomic
  8. import "sync/atomic"
  9. // Interface represents atomic operations on a value.
  10. type Interface[T any] interface {
  11. // Load value atomically.
  12. Load() T
  13. // Store value atomically.
  14. Store(value T)
  15. // Swap the previous value with the new value atomically.
  16. Swap(new T) (old T)
  17. // CompareAndSwap the previous value with new if its value is "old".
  18. CompareAndSwap(old, new T) (swapped bool)
  19. }
  20. var _ Interface[bool] = &Value[bool]{}
  21. // Value wraps any generic value in atomic load and store operations.
  22. type Value[T any] struct {
  23. value atomic.Value
  24. }
  25. // New atomic Value.
  26. func New[T any](seed T) *Value[T] {
  27. v := &Value[T]{}
  28. v.value.Store(seed)
  29. return v
  30. }
  31. func (v *Value[T]) Load() (out T) {
  32. value := v.value.Load()
  33. if value == nil {
  34. return out
  35. }
  36. return value.(T)
  37. }
  38. func (v *Value[T]) Store(value T) { v.value.Store(value) }
  39. func (v *Value[T]) Swap(new T) (old T) { return v.value.Swap(new).(T) }
  40. func (v *Value[T]) CompareAndSwap(old, new T) (swapped bool) { return v.value.CompareAndSwap(old, new) }
  41. // atomicint defines the types that atomic integer operations are supported on.
  42. type atomicint interface {
  43. int32 | uint32 | int64 | uint64
  44. }
  45. // Int expresses atomic operations on signed or unsigned integer values.
  46. type Int[T atomicint] interface {
  47. Interface[T]
  48. // Add a value and return the new result.
  49. Add(delta T) (new T)
  50. }
  51. // Currently not supported by Go's generic type system:
  52. //
  53. // ./atomic.go:48:9: cannot use type switch on type parameter value v (variable of type T constrained by atomicint)
  54. //
  55. // // ForInt infers and creates an atomic Int[T] type for a value.
  56. // func ForInt[T atomicint](v T) Int[T] {
  57. // switch v.(type) {
  58. // case int32:
  59. // return NewInt32(v)
  60. // case uint32:
  61. // return NewUint32(v)
  62. // case int64:
  63. // return NewInt64(v)
  64. // case uint64:
  65. // return NewUint64(v)
  66. // }
  67. // panic("can't happen")
  68. // }
  69. // Int32 atomic value.
  70. //
  71. // Copying creates an alias. The zero value is not usable, use NewInt32.
  72. type Int32 struct{ value *int32 }
  73. // NewInt32 creates a new atomic integer with an initial value.
  74. func NewInt32(value int32) Int32 { return Int32{value: &value} }
  75. var _ Int[int32] = &Int32{}
  76. func (i Int32) Add(delta int32) (new int32) { return atomic.AddInt32(i.value, delta) }
  77. func (i Int32) Load() (val int32) { return atomic.LoadInt32(i.value) }
  78. func (i Int32) Store(val int32) { atomic.StoreInt32(i.value, val) }
  79. func (i Int32) Swap(new int32) (old int32) { return atomic.SwapInt32(i.value, new) }
  80. func (i Int32) CompareAndSwap(old, new int32) (swapped bool) {
  81. return atomic.CompareAndSwapInt32(i.value, old, new)
  82. }
  83. // Uint32 atomic value.
  84. //
  85. // Copying creates an alias.
  86. type Uint32 struct{ value *uint32 }
  87. var _ Int[uint32] = Uint32{}
  88. // NewUint32 creates a new atomic integer with an initial value.
  89. func NewUint32(value uint32) Uint32 { return Uint32{value: &value} }
  90. func (i Uint32) Add(delta uint32) (new uint32) { return atomic.AddUint32(i.value, delta) }
  91. func (i Uint32) Load() (val uint32) { return atomic.LoadUint32(i.value) }
  92. func (i Uint32) Store(val uint32) { atomic.StoreUint32(i.value, val) }
  93. func (i Uint32) Swap(new uint32) (old uint32) { return atomic.SwapUint32(i.value, new) }
  94. func (i Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
  95. return atomic.CompareAndSwapUint32(i.value, old, new)
  96. }
  97. // Int64 atomic value.
  98. //
  99. // Copying creates an alias.
  100. type Int64 struct{ value *int64 }
  101. var _ Int[int64] = Int64{}
  102. // NewInt64 creates a new atomic integer with an initial value.
  103. func NewInt64(value int64) Int64 { return Int64{value: &value} }
  104. func (i Int64) Add(delta int64) (new int64) { return atomic.AddInt64(i.value, delta) }
  105. func (i Int64) Load() (val int64) { return atomic.LoadInt64(i.value) }
  106. func (i Int64) Store(val int64) { atomic.StoreInt64(i.value, val) }
  107. func (i Int64) Swap(new int64) (old int64) { return atomic.SwapInt64(i.value, new) }
  108. func (i Int64) CompareAndSwap(old, new int64) (swapped bool) {
  109. return atomic.CompareAndSwapInt64(i.value, old, new)
  110. }
  111. // Uint64 atomic value.
  112. //
  113. // Copying creates an alias.
  114. type Uint64 struct{ value *uint64 }
  115. var _ Int[uint64] = Uint64{}
  116. // NewUint64 creates a new atomic integer with an initial value.
  117. func NewUint64(value uint64) Uint64 { return Uint64{value: &value} }
  118. func (i Uint64) Add(delta uint64) (new uint64) { return atomic.AddUint64(i.value, delta) }
  119. func (i Uint64) Load() (val uint64) { return atomic.LoadUint64(i.value) }
  120. func (i Uint64) Store(val uint64) { atomic.StoreUint64(i.value, val) }
  121. func (i Uint64) Swap(new uint64) (old uint64) { return atomic.SwapUint64(i.value, new) }
  122. func (i Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
  123. return atomic.CompareAndSwapUint64(i.value, old, new)
  124. }