Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package internal
- import (
- "sync"
- "sync/atomic"
- )
- // Once is an object that will perform exactly one action unless the action fails.
- type Once struct {
- // done indicates whether the action has been performed.
- // It is first in the struct because it is used in the hot path.
- // The hot path is inlined at every call site.
- // Placing done first allows more compact instructions on some architectures (amd64/x86),
- // and fewer instructions (to calculate offset) on other architectures.
- done uint32
- m sync.Mutex
- }
- // TryDo calls the function f if and only if TryDo is being called for the
- // first time for this instance of Once. In other words, given
- // var once Once
- // if once.TryDo(f) is called multiple times, only the first call will invoke f,
- // unless the invocation results in an error A new instance of
- // Once is required for each function to execute.
- //
- // TryDo is intended for initialization that must be run exactly once. Since f
- // is niladic, it may be necessary to use a function literal to capture the
- // arguments to a function to be invoked by Do:
- // config.once.TryDo(func() error { return config.init(filename) })
- //
- // Because no call to TryDo returns until the one call to f returns, if f causes
- // TryDo to be called, it will deadlock.
- //
- // If f panics, TryDo considers it to have failed.
- //
- func (o *Once) TryDo(f func() error) error {
- // Note: Here is an incorrect implementation of Do:
- //
- // if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
- // f()
- // }
- //
- // TryDo guarantees that when it returns, f has finished.
- // This implementation would not implement that guarantee:
- // given two simultaneous calls, the winner of the cas would
- // call f, and the second would return immediately, without
- // waiting for the first's call to f to complete.
- // This is why the slow path falls back to a mutex, and why
- // the atomic.StoreUint32 must be delayed until after f returns.
- if atomic.LoadUint32(&o.done) == 0 {
- // Outlined slow-path to allow inlining of the fast-path.
- return o.doSlow(f)
- }
- return nil
- }
- func (o *Once) doSlow(f func() error) error {
- o.m.Lock()
- defer o.m.Unlock()
- if o.done == 0 {
- if err := f(); err != nil {
- return err
- }
- defer atomic.StoreUint32(&o.done, 1)
- }
- return nil
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement