mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-05 07:55:36 +00:00
89 lines
2.2 KiB
Go
89 lines
2.2 KiB
Go
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
// Package lazy provides types for lazily initialized values.
|
||
|
package lazy
|
||
|
|
||
|
import "sync"
|
||
|
|
||
|
// SyncValue is a lazily computed value.
|
||
|
//
|
||
|
// Use either Get or GetErr, depending on whether your fill function returns an
|
||
|
// error.
|
||
|
//
|
||
|
// Recursive use of a SyncValue from its own fill function will deadlock.
|
||
|
//
|
||
|
// SyncValue is safe for concurrent use.
|
||
|
type SyncValue[T any] struct {
|
||
|
once sync.Once
|
||
|
v T
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
// Set attempts to set z's value to val, and reports whether it succeeded.
|
||
|
// Set only succeeds if none of Get/GetErr/Set have been called before.
|
||
|
func (z *SyncValue[T]) Set(val T) bool {
|
||
|
var wasSet bool
|
||
|
z.once.Do(func() {
|
||
|
z.v = val
|
||
|
wasSet = true
|
||
|
})
|
||
|
return wasSet
|
||
|
}
|
||
|
|
||
|
// MustSet sets z's value to val, or panics if z already has a value.
|
||
|
func (z *SyncValue[T]) MustSet(val T) {
|
||
|
if !z.Set(val) {
|
||
|
panic("Set after already filled")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get returns z's value, calling fill to compute it if necessary.
|
||
|
// f is called at most once.
|
||
|
func (z *SyncValue[T]) Get(fill func() T) T {
|
||
|
z.once.Do(func() { z.v = fill() })
|
||
|
return z.v
|
||
|
}
|
||
|
|
||
|
// GetErr returns z's value, calling fill to compute it if necessary.
|
||
|
// f is called at most once, and z remembers both of fill's outputs.
|
||
|
func (z *SyncValue[T]) GetErr(fill func() (T, error)) (T, error) {
|
||
|
z.once.Do(func() { z.v, z.err = fill() })
|
||
|
return z.v, z.err
|
||
|
}
|
||
|
|
||
|
// SyncFunc wraps a function to make it lazy.
|
||
|
//
|
||
|
// The returned function calls fill the first time it's called, and returns
|
||
|
// fill's result on every subsequent call.
|
||
|
//
|
||
|
// The returned function is safe for concurrent use.
|
||
|
func SyncFunc[T any](fill func() T) func() T {
|
||
|
var (
|
||
|
once sync.Once
|
||
|
v T
|
||
|
)
|
||
|
return func() T {
|
||
|
once.Do(func() { v = fill() })
|
||
|
return v
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SyncFuncErr wraps a function to make it lazy.
|
||
|
//
|
||
|
// The returned function calls fill the first time it's called, and returns
|
||
|
// fill's results on every subsequent call.
|
||
|
//
|
||
|
// The returned function is safe for concurrent use.
|
||
|
func SyncFuncErr[T any](fill func() (T, error)) func() (T, error) {
|
||
|
var (
|
||
|
once sync.Once
|
||
|
v T
|
||
|
err error
|
||
|
)
|
||
|
return func() (T, error) {
|
||
|
once.Do(func() { v, err = fill() })
|
||
|
return v, err
|
||
|
}
|
||
|
}
|