github.com/haraldrudell/parl@v0.4.176/atomic-min.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "sync" 10 "sync/atomic" 11 12 "golang.org/x/exp/constraints" 13 ) 14 15 // AtomicMin is a thread-safe container for a minimum value of any integer type 16 // - hasValue indicator 17 // - generic for any underlying Integer type 18 // - if type is signed, min may be negative 19 // - lock for first Value invocation 20 // - initialization-free 21 type AtomicMin[T constraints.Integer] struct { 22 isInitialized atomic.Bool // whether a value is present 23 value atomic.Uint64 // current min value as uint64 24 initLock sync.Mutex // thread selector and wait for wtriting initial value 25 } 26 27 // Value notes a new min-candidate 28 // - if not a new minima, state is not changed 29 // - Thread-safe 30 func (a *AtomicMin[T]) Value(value T) (isNewMin bool) { 31 32 // value-valueU64 is candidate min-value 33 var valueU64 uint64 = uint64(value) 34 35 // ensure initialized 36 if !a.isInitialized.Load() { 37 if isNewMin = a.init(valueU64); isNewMin { 38 return // this thread set initial value return 39 } 40 } 41 42 // aggregate minimum 43 var current = a.value.Load() 44 var currentT = T(current) 45 // make comparison in T domain 46 if isNewMin = value < currentT; !isNewMin { 47 return // too large value, nothing to do return 48 } 49 50 // ensure write of new min value 51 for { 52 53 // try to write 54 if a.value.CompareAndSwap(current, valueU64) { 55 return // min-value updated return 56 } 57 58 // load new copy of value 59 current = a.value.Load() 60 currentT = T(current) 61 if currentT <= value { 62 return // ok min-value written by other thread return 63 } 64 } 65 } 66 67 // Min returns current minimum value and a flag whether a value is present 68 // - Thread-safe 69 func (a *AtomicMin[T]) Min() (value T, hasValue bool) { 70 if hasValue = a.isInitialized.Load(); !hasValue { 71 return // no min yet return 72 } 73 value = T(a.value.Load()) 74 return 75 } 76 77 // init uses lock to have loser threads wait until winner thread has updated value 78 func (a *AtomicMin[T]) init(valueU64 uint64) (didStore bool) { 79 a.initLock.Lock() 80 defer a.initLock.Unlock() 81 82 if didStore = !a.isInitialized.Load(); !didStore { 83 return // another thread was first 84 } 85 a.value.Store(valueU64) 86 a.isInitialized.Store(true) 87 return 88 }