Source file src/sync/oncefunc.go

     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  		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.
    43  func 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.
    74  func 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  

View as plain text