oncefunc.mx raw

   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