| // 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() { | |
| // Use a struct so that there's a single heap allocation. | |
| d := struct { | |
| f func() | |
| once Once | |
| valid bool | |
| p any | |
| }{ | |
| f: f, | |
| } | |
| return func() { | |
| d.once.Do(func() { | |
| defer func() { | |
| d.f = nil // Do not keep f alive after invoking it. | |
| d.p = recover() | |
| if !d.valid { | |
| // Re-panic immediately so on the first | |
| // call the user gets a complete stack | |
| // trace into f. | |
| panic(d.p) | |
| } | |
| }() | |
| d.f() | |
| d.valid = true // Set only if f does not panic. | |
| }) | |
| if !d.valid { | |
| panic(d.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 { | |
| // Use a struct so that there's a single heap allocation. | |
| d := struct { | |
| f func() T | |
| once Once | |
| valid bool | |
| p any | |
| result T | |
| }{ | |
| f: f, | |
| } | |
| return func() T { | |
| d.once.Do(func() { | |
| defer func() { | |
| d.f = nil | |
| d.p = recover() | |
| if !d.valid { | |
| panic(d.p) | |
| } | |
| }() | |
| d.result = d.f() | |
| d.valid = true | |
| }) | |
| if !d.valid { | |
| panic(d.p) | |
| } | |
| return d.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) { | |
| // Use a struct so that there's a single heap allocation. | |
| d := struct { | |
| f func() (T1, T2) | |
| once Once | |
| valid bool | |
| p any | |
| r1 T1 | |
| r2 T2 | |
| }{ | |
| f: f, | |
| } | |
| return func() (T1, T2) { | |
| d.once.Do(func() { | |
| defer func() { | |
| d.f = nil | |
| d.p = recover() | |
| if !d.valid { | |
| panic(d.p) | |
| } | |
| }() | |
| d.r1, d.r2 = d.f() | |
| d.valid = true | |
| }) | |
| if !d.valid { | |
| panic(d.p) | |
| } | |
| return d.r1, d.r2 | |
| } | |
| } | |