azkustodata/utils/once.go (71 lines of code) (raw):
package utils
import (
"sync"
"sync/atomic"
)
type Once[Out any] interface {
Do(f func() (Out, error)) (Out, error)
Done() bool
Result() (bool, Out, error)
}
type OnceWithInit[Out any] interface {
Once[Out]
DoWithInit() (Out, error)
}
type once[Out any] struct {
mutex sync.Mutex
result Out
err error
done uint32
}
type onceWithInit[Out any] struct {
inner Once[Out]
f func() (Out, error)
}
func NewOnce[Out any]() Once[Out] {
var empty Out
return &once[Out]{
mutex: sync.Mutex{},
result: empty,
err: nil,
done: 0,
}
}
func NewOnceWithInit[Out any](f func() (Out, error)) OnceWithInit[Out] {
return &onceWithInit[Out]{
inner: NewOnce[Out](),
f: f,
}
}
func (o *onceWithInit[Out]) DoWithInit() (Out, error) {
return o.inner.Do(o.f)
}
func (o *onceWithInit[Out]) Do(f func() (Out, error)) (Out, error) {
return o.inner.Do(f)
}
func (o *onceWithInit[Out]) Done() bool {
return o.inner.Done()
}
func (o *onceWithInit[Out]) Result() (bool, Out, error) {
return o.inner.Result()
}
func (o *once[Out]) Do(f func() (Out, error)) (Out, error) {
if atomic.LoadUint32(&o.done) != 0 {
return o.result, o.err
}
o.mutex.Lock()
defer o.mutex.Unlock()
if o.done == 0 {
o.result, o.err = f()
if o.err == nil {
defer atomic.StoreUint32(&o.done, 1)
}
}
return o.result, o.err
}
func (o *once[Out]) Done() bool {
return o.done != 0
}
func (o *once[Out]) Result() (bool, Out, error) {
return o.Done(), o.result, o.err
}