github.com/haraldrudell/parl@v0.4.176/internal/cyclebreaker/atomic-max.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package cyclebreaker
     7  
     8  import (
     9  	"sync/atomic"
    10  
    11  	"github.com/haraldrudell/parl/ints"
    12  	"golang.org/x/exp/constraints"
    13  )
    14  
    15  type AtomicMax[T constraints.Integer] struct {
    16  	value    uint64
    17  	hasValue atomic.Bool
    18  }
    19  
    20  func NewAtomicMax[T constraints.Integer](value T) (atomicMax *AtomicMax[T]) {
    21  	m := AtomicMax[T]{}
    22  	m.value = uint64(value) // set initial threshold
    23  	return &m
    24  }
    25  
    26  func (max *AtomicMax[T]) Value(value T) (isNewMax bool) {
    27  
    28  	// check if value is a new max
    29  	valueU64, err := ints.Unsigned[uint64](value, "")
    30  	if err != nil {
    31  		panic(err) // value out of range, ie. negative
    32  	}
    33  	maxU64p := (*uint64)(&max.value)
    34  	current := atomic.LoadUint64(maxU64p)
    35  	if isNewMax = valueU64 > current; !isNewMax {
    36  		return // not a new max return
    37  	}
    38  	max.hasValue.Store(true)
    39  
    40  	// store the new max
    41  	for {
    42  
    43  		// try to write value to *max
    44  		if atomic.CompareAndSwapUint64(maxU64p, current, valueU64) {
    45  			return // new max written return
    46  		}
    47  		if current = atomic.LoadUint64(maxU64p); current >= valueU64 {
    48  			return // no longer a need to write return
    49  		}
    50  	}
    51  }
    52  
    53  func (max *AtomicMax[T]) Max() (value T, hasValue bool) {
    54  	value = T(atomic.LoadUint64((*uint64)(&max.value)))
    55  	hasValue = max.hasValue.Load()
    56  	return
    57  }
    58  
    59  func (max *AtomicMax[T]) Max1() (value T) {
    60  	return T(atomic.LoadUint64((*uint64)(&max.value)))
    61  }