1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package sync
6 7 // OnceFunc returns a function that invokes f only once. The returned function
8 // may be called concurrently.
9 //
10 // If f panics, the returned function will panic with the same value on every call.
11 func OnceFunc(f func()) func() {
12 var (
13 once Once
14 valid bool
15 p any
16 )
17 // Construct the inner closure just once to reduce costs on the fast path.
18 g := func() {
19 defer func() {
20 p = recover()
21 if !valid {
22 // Re-panic immediately so on the first call the user gets a
23 // complete stack trace into f.
24 panic(p)
25 }
26 }()
27 f()
28 valid = true // Set only if f does not panic
29 }
30 return func() {
31 once.Do(g)
32 if !valid {
33 panic(p)
34 }
35 }
36 }
37 38 // OnceValue returns a function that invokes f only once and returns the value
39 // returned by f. The returned function may be called concurrently.
40 //
41 // If f panics, the returned function will panic with the same value on every call.
42 func OnceValue[T any](f func() T) func() T {
43 var (
44 once Once
45 valid bool
46 p any
47 result T
48 )
49 g := func() {
50 defer func() {
51 p = recover()
52 if !valid {
53 panic(p)
54 }
55 }()
56 result = f()
57 valid = true
58 }
59 return func() T {
60 once.Do(g)
61 if !valid {
62 panic(p)
63 }
64 return result
65 }
66 }
67 68 // OnceValues returns a function that invokes f only once and returns the values
69 // returned by f. The returned function may be called concurrently.
70 //
71 // If f panics, the returned function will panic with the same value on every call.
72 func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
73 var (
74 once Once
75 valid bool
76 p any
77 r1 T1
78 r2 T2
79 )
80 g := func() {
81 defer func() {
82 p = recover()
83 if !valid {
84 panic(p)
85 }
86 }()
87 r1, r2 = f()
88 valid = true
89 }
90 return func() (T1, T2) {
91 once.Do(g)
92 if !valid {
93 panic(p)
94 }
95 return r1, r2
96 }
97 }
98