// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package sync import ( "sync/atomic" ) // OnceFunc returns a function that invokes f only once. The returned function // may be called concurrently. // // If f panics, the returned function will panic with the same value on every call. func OnceFunc(f func()) func() { var ( once Once valid bool p any ) // Construct the inner closure just once to reduce costs on the fast path. g := func() { defer func() { p = recover() if !valid { // Re-panic immediately so on the first call the user gets a // complete stack trace into f. panic(p) } }() f() valid = true // Set only if f does not panic } return func() { once.Do(g) if !valid { panic(p) } } } // OnceValue returns a function that invokes f only once and returns the value // returned by f. The returned function may be called concurrently. // // If f panics, the returned function will panic with the same value on every call. func OnceValue[T any](f func() T) func() T { var ( once Once valid bool p any result T ) g := func() { defer func() { p = recover() if !valid { panic(p) } }() result = f() valid = true } return func() T { once.Do(g) if !valid { panic(p) } return result } } // OnceValues returns a function that invokes f only once and returns the values // returned by f. The returned function may be called concurrently. // // If f panics, the returned function will panic with the same value on every call. func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { var ( once Once valid bool p any r1 T1 r2 T2 ) g := func() { defer func() { p = recover() if !valid { panic(p) } }() r1, r2 = f() valid = true } return func() (T1, T2) { once.Do(g) if !valid { panic(p) } return r1, r2 } } // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //package sync // import ( // "sync/atomic" // ) // Once is an object that will perform exactly one action. // // A Once must not be copied after first use. // // In the terminology of the Go memory model, // the return from f “synchronizes before” // the return from any call of once.Do(f). 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/386), // and fewer instructions (to calculate offset) on other architectures. done uint32 m Mutex } // Do calls the function f if and only if Do is being called for the // first time for this instance of Once. In other words, given // // var once Once // // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of // Once is required for each function to execute. // // Do 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.Do(func() { config.init(filename) }) // // Because no call to Do returns until the one call to f returns, if f causes // Do to be called, it will deadlock. // // If f panics, Do considers it to have returned; future calls of Do return // without calling f. func (o *Once) Do(f func()) { // Note: Here is an incorrect implementation of Do: // // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { // f() // } // // Do 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. o.doSlow(f) } } func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }