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 5package 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. 11func 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 f = nil // Do not keep f alive after invoking it. 29 valid = true // Set only if f does not panic. 30 } 31 return func() { 32 once.Do(g) 33 if !valid { 34 panic(p) 35 } 36 } 37} 38 39// OnceValue returns a function that invokes f only once and returns the value 40// returned by f. The returned function may be called concurrently. 41// 42// If f panics, the returned function will panic with the same value on every call. 43func OnceValue[T any](f func() T) func() T { 44 var ( 45 once Once 46 valid bool 47 p any 48 result T 49 ) 50 g := func() { 51 defer func() { 52 p = recover() 53 if !valid { 54 panic(p) 55 } 56 }() 57 result = f() 58 f = nil 59 valid = true 60 } 61 return func() T { 62 once.Do(g) 63 if !valid { 64 panic(p) 65 } 66 return result 67 } 68} 69 70// OnceValues returns a function that invokes f only once and returns the values 71// returned by f. The returned function may be called concurrently. 72// 73// If f panics, the returned function will panic with the same value on every call. 74func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { 75 var ( 76 once Once 77 valid bool 78 p any 79 r1 T1 80 r2 T2 81 ) 82 g := func() { 83 defer func() { 84 p = recover() 85 if !valid { 86 panic(p) 87 } 88 }() 89 r1, r2 = f() 90 f = nil 91 valid = true 92 } 93 return func() (T1, T2) { 94 once.Do(g) 95 if !valid { 96 panic(p) 97 } 98 return r1, r2 99 } 100} 101