mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 00:55:11 +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
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |