| // 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 |
| |
| // 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() |
| f = nil // Do not keep f alive after invoking it. |
| 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() |
| f = nil |
| 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() |
| f = nil |
| valid = true |
| } |
| return func() (T1, T2) { |
| once.Do(g) |
| if !valid { |
| panic(p) |
| } |
| return r1, r2 |
| } |
| } |