github.com/haraldrudell/parl@v0.4.176/pruntime/cacher.go (about)

     1  /*
     2  © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pruntime
     7  
     8  import (
     9  	"sync"
    10  	"sync/atomic"
    11  )
    12  
    13  // CacheMechanic ensures that an init function is executed exactly once
    14  //   - sync.Once using a known number of stack frames
    15  //   - initialization-free
    16  type CacheMechanic struct {
    17  	// first access makes threads wait until data is available
    18  	//	- subsequent accesses is atomic performance
    19  	initLock sync.Mutex
    20  	// written inside lock
    21  	//	- isReady read provides happens-before
    22  	isReady atomic.Bool
    23  }
    24  
    25  // EnsureInit ensures data is loaded exactly once
    26  //   - initFunc loads data
    27  //   - invocations after first EnsureInit return are atomic performance
    28  //   - first invocation is locked performance
    29  //   - subsequent invocations prior to first EnsureInit return are held waiting
    30  //   - —
    31  //   - upon return, it is guaranteed that init has completed
    32  //   - order of thread-returns is not guaranteed
    33  func (c *CacheMechanic) EnsureInit(initFunc func()) {
    34  
    35  	// ouside lock fast check
    36  	if c.isReady.Load() {
    37  		return // already initialized
    38  	}
    39  
    40  	// first thread will win, other threads will wait
    41  	c.initLock.Lock()
    42  	defer c.initLock.Unlock()
    43  
    44  	// inside lock check
    45  	if c.isReady.Load() {
    46  		return // already initialized by other thread
    47  	}
    48  
    49  	// winner thread does init
    50  	initFunc()
    51  
    52  	// flag values available
    53  	c.isReady.Store(true)
    54  }