// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sync

// OnceFunc returns a function that invokes f only once. The returned function
// may be called concurrently.
//
// If f panics, the returned function will panic with the same value on every call.
func ( func()) func() {
	// Use a struct so that there's a single heap allocation.
	 := struct {
		     func()
		  Once
		 bool
		     any
	}{
		: ,
	}
	return func() {
		..Do(func() {
			defer func() {
				. = nil // Do not keep f alive after invoking it.
				. = recover()
				if !. {
					// Re-panic immediately so on the first
					// call the user gets a complete stack
					// trace into f.
					panic(.)
				}
			}()
			.()
			. = true // Set only if f does not panic.
		})
		if !. {
			panic(.)
		}
	}
}

// OnceValue returns a function that invokes f only once and returns the value
// returned by f. The returned function may be called concurrently.
//
// If f panics, the returned function will panic with the same value on every call.
func [ any]( func() ) func()  {
	// Use a struct so that there's a single heap allocation.
	 := struct {
		      func() 
		   Once
		  bool
		      any
		 
	}{
		: ,
	}
	return func()  {
		..Do(func() {
			defer func() {
				. = nil
				. = recover()
				if !. {
					panic(.)
				}
			}()
			. = .()
			. = true
		})
		if !. {
			panic(.)
		}
		return .
	}
}

// OnceValues returns a function that invokes f only once and returns the values
// returned by f. The returned function may be called concurrently.
//
// If f panics, the returned function will panic with the same value on every call.
func [,  any]( func() (, )) func() (, ) {
	// Use a struct so that there's a single heap allocation.
	 := struct {
		     func() (, )
		  Once
		 bool
		     any
		    
		    
	}{
		: ,
	}
	return func() (, ) {
		..Do(func() {
			defer func() {
				. = nil
				. = recover()
				if !. {
					panic(.)
				}
			}()
			., . = .()
			. = true
		})
		if !. {
			panic(.)
		}
		return ., .
	}
}