github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/sync/oncefunc.go (about) 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 }