Update dependencies
This commit is contained in:
32
vendor/tailscale.com/syncs/locked.go
generated
vendored
Normal file
32
vendor/tailscale.com/syncs/locked.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// AssertLocked panics if m is not locked.
|
||||
func AssertLocked(m *sync.Mutex) {
|
||||
if m.TryLock() {
|
||||
m.Unlock()
|
||||
panic("mutex is not locked")
|
||||
}
|
||||
}
|
||||
|
||||
// AssertRLocked panics if rw is not locked for reading or writing.
|
||||
func AssertRLocked(rw *sync.RWMutex) {
|
||||
if rw.TryLock() {
|
||||
rw.Unlock()
|
||||
panic("mutex is not locked")
|
||||
}
|
||||
}
|
||||
|
||||
// AssertWLocked panics if rw is not locked for writing.
|
||||
func AssertWLocked(rw *sync.RWMutex) {
|
||||
if rw.TryRLock() {
|
||||
rw.RUnlock()
|
||||
panic("mutex is not rlocked")
|
||||
}
|
||||
}
|
||||
31
vendor/tailscale.com/syncs/pool.go
generated
vendored
Normal file
31
vendor/tailscale.com/syncs/pool.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package syncs
|
||||
|
||||
import "sync"
|
||||
|
||||
// Pool is the generic version of [sync.Pool].
|
||||
type Pool[T any] struct {
|
||||
pool sync.Pool
|
||||
|
||||
// New optionally specifies a function to generate
|
||||
// a value when Get would otherwise return the zero value of T.
|
||||
// It may not be changed concurrently with calls to Get.
|
||||
New func() T
|
||||
}
|
||||
|
||||
// Get selects an arbitrary item from the Pool, removes it from the Pool,
|
||||
// and returns it to the caller. See [sync.Pool.Get].
|
||||
func (p *Pool[T]) Get() T {
|
||||
x, ok := p.pool.Get().(T)
|
||||
if !ok && p.New != nil {
|
||||
x = p.New()
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Put adds x to the pool.
|
||||
func (p *Pool[T]) Put(x T) {
|
||||
p.pool.Put(x)
|
||||
}
|
||||
138
vendor/tailscale.com/syncs/shardedmap.go
generated
vendored
Normal file
138
vendor/tailscale.com/syncs/shardedmap.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// ShardedMap is a synchronized map[K]V, internally sharded by a user-defined
|
||||
// K-sharding function.
|
||||
//
|
||||
// The zero value is not safe for use; use NewShardedMap.
|
||||
type ShardedMap[K comparable, V any] struct {
|
||||
shardFunc func(K) int
|
||||
shards []mapShard[K, V]
|
||||
}
|
||||
|
||||
type mapShard[K comparable, V any] struct {
|
||||
mu sync.Mutex
|
||||
m map[K]V
|
||||
_ cpu.CacheLinePad // avoid false sharing of neighboring shards' mutexes
|
||||
}
|
||||
|
||||
// NewShardedMap returns a new ShardedMap with the given number of shards and
|
||||
// sharding function.
|
||||
//
|
||||
// The shard func must return a integer in the range [0, shards) purely
|
||||
// deterministically based on the provided K.
|
||||
func NewShardedMap[K comparable, V any](shards int, shard func(K) int) *ShardedMap[K, V] {
|
||||
m := &ShardedMap[K, V]{
|
||||
shardFunc: shard,
|
||||
shards: make([]mapShard[K, V], shards),
|
||||
}
|
||||
for i := range m.shards {
|
||||
m.shards[i].m = make(map[K]V)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *ShardedMap[K, V]) shard(key K) *mapShard[K, V] {
|
||||
return &m.shards[m.shardFunc(key)]
|
||||
}
|
||||
|
||||
// GetOk returns m[key] and whether it was present.
|
||||
func (m *ShardedMap[K, V]) GetOk(key K) (value V, ok bool) {
|
||||
shard := m.shard(key)
|
||||
shard.mu.Lock()
|
||||
defer shard.mu.Unlock()
|
||||
value, ok = shard.m[key]
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns m[key] or the zero value of V if key is not present.
|
||||
func (m *ShardedMap[K, V]) Get(key K) (value V) {
|
||||
value, _ = m.GetOk(key)
|
||||
return
|
||||
}
|
||||
|
||||
// Mutate atomically mutates m[k] by calling mutator.
|
||||
//
|
||||
// The mutator function is called with the old value (or its zero value) and
|
||||
// whether it existed in the map and it returns the new value and whether it
|
||||
// should be set in the map (true) or deleted from the map (false).
|
||||
//
|
||||
// It returns the change in size of the map as a result of the mutation, one of
|
||||
// -1 (delete), 0 (change), or 1 (addition).
|
||||
func (m *ShardedMap[K, V]) Mutate(key K, mutator func(oldValue V, oldValueExisted bool) (newValue V, keep bool)) (sizeDelta int) {
|
||||
shard := m.shard(key)
|
||||
shard.mu.Lock()
|
||||
defer shard.mu.Unlock()
|
||||
oldV, oldOK := shard.m[key]
|
||||
newV, newOK := mutator(oldV, oldOK)
|
||||
if newOK {
|
||||
shard.m[key] = newV
|
||||
if oldOK {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
delete(shard.m, key)
|
||||
if oldOK {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Set sets m[key] = value.
|
||||
//
|
||||
// present in m).
|
||||
func (m *ShardedMap[K, V]) Set(key K, value V) (grew bool) {
|
||||
shard := m.shard(key)
|
||||
shard.mu.Lock()
|
||||
defer shard.mu.Unlock()
|
||||
s0 := len(shard.m)
|
||||
shard.m[key] = value
|
||||
return len(shard.m) > s0
|
||||
}
|
||||
|
||||
// Delete removes key from m.
|
||||
//
|
||||
// It reports whether the map size shrunk (that is, whether key was present in
|
||||
// the map).
|
||||
func (m *ShardedMap[K, V]) Delete(key K) (shrunk bool) {
|
||||
shard := m.shard(key)
|
||||
shard.mu.Lock()
|
||||
defer shard.mu.Unlock()
|
||||
s0 := len(shard.m)
|
||||
delete(shard.m, key)
|
||||
return len(shard.m) < s0
|
||||
}
|
||||
|
||||
// Contains reports whether m contains key.
|
||||
func (m *ShardedMap[K, V]) Contains(key K) bool {
|
||||
shard := m.shard(key)
|
||||
shard.mu.Lock()
|
||||
defer shard.mu.Unlock()
|
||||
_, ok := shard.m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Len returns the number of elements in m.
|
||||
//
|
||||
// It does so by locking shards one at a time, so it's not particularly cheap,
|
||||
// nor does it give a consistent snapshot of the map. It's mostly intended for
|
||||
// metrics or testing.
|
||||
func (m *ShardedMap[K, V]) Len() int {
|
||||
n := 0
|
||||
for i := range m.shards {
|
||||
shard := &m.shards[i]
|
||||
shard.mu.Lock()
|
||||
n += len(shard.m)
|
||||
shard.mu.Unlock()
|
||||
}
|
||||
return n
|
||||
}
|
||||
352
vendor/tailscale.com/syncs/syncs.go
generated
vendored
Normal file
352
vendor/tailscale.com/syncs/syncs.go
generated
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package syncs contains additional sync types and functionality.
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"iter"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"tailscale.com/util/mak"
|
||||
)
|
||||
|
||||
// ClosedChan returns a channel that's already closed.
|
||||
func ClosedChan() <-chan struct{} { return closedChan }
|
||||
|
||||
var closedChan = initClosedChan()
|
||||
|
||||
func initClosedChan() <-chan struct{} {
|
||||
ch := make(chan struct{})
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// AtomicValue is the generic version of [atomic.Value].
|
||||
type AtomicValue[T any] struct {
|
||||
v atomic.Value
|
||||
}
|
||||
|
||||
// wrappedValue is used to wrap a value T in a concrete type,
|
||||
// otherwise atomic.Value.Store may panic due to mismatching types in interfaces.
|
||||
// This wrapping is not necessary for non-interface kinds of T,
|
||||
// but there is no harm in wrapping anyways.
|
||||
// See https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/sync/atomic/value.go;l=78
|
||||
type wrappedValue[T any] struct{ v T }
|
||||
|
||||
// Load returns the value set by the most recent Store.
|
||||
// It returns the zero value for T if the value is empty.
|
||||
func (v *AtomicValue[T]) Load() T {
|
||||
x, _ := v.LoadOk()
|
||||
return x
|
||||
}
|
||||
|
||||
// LoadOk is like Load but returns a boolean indicating whether the value was
|
||||
// loaded.
|
||||
func (v *AtomicValue[T]) LoadOk() (_ T, ok bool) {
|
||||
x := v.v.Load()
|
||||
if x != nil {
|
||||
return x.(wrappedValue[T]).v, true
|
||||
}
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// Store sets the value of the Value to x.
|
||||
func (v *AtomicValue[T]) Store(x T) {
|
||||
v.v.Store(wrappedValue[T]{x})
|
||||
}
|
||||
|
||||
// Swap stores new into Value and returns the previous value.
|
||||
// It returns the zero value for T if the value is empty.
|
||||
func (v *AtomicValue[T]) Swap(x T) (old T) {
|
||||
oldV := v.v.Swap(wrappedValue[T]{x})
|
||||
if oldV != nil {
|
||||
return oldV.(wrappedValue[T]).v
|
||||
}
|
||||
return old
|
||||
}
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for the Value.
|
||||
func (v *AtomicValue[T]) CompareAndSwap(oldV, newV T) (swapped bool) {
|
||||
return v.v.CompareAndSwap(wrappedValue[T]{oldV}, wrappedValue[T]{newV})
|
||||
}
|
||||
|
||||
// WaitGroupChan is like a sync.WaitGroup, but has a chan that closes
|
||||
// on completion that you can wait on. (This, you can only use the
|
||||
// value once)
|
||||
// Also, its zero value is not usable. Use the constructor.
|
||||
type WaitGroupChan struct {
|
||||
n int64 // atomic
|
||||
done chan struct{} // closed on transition to zero
|
||||
}
|
||||
|
||||
// NewWaitGroupChan returns a new single-use WaitGroupChan.
|
||||
func NewWaitGroupChan() *WaitGroupChan {
|
||||
return &WaitGroupChan{done: make(chan struct{})}
|
||||
}
|
||||
|
||||
// DoneChan returns a channel that's closed on completion.
|
||||
func (wg *WaitGroupChan) DoneChan() <-chan struct{} { return wg.done }
|
||||
|
||||
// Add adds delta, which may be negative, to the WaitGroupChan
|
||||
// counter. If the counter becomes zero, all goroutines blocked on
|
||||
// Wait or the Done chan are released. If the counter goes negative,
|
||||
// Add panics.
|
||||
//
|
||||
// Note that calls with a positive delta that occur when the counter
|
||||
// is zero must happen before a Wait. Calls with a negative delta, or
|
||||
// calls with a positive delta that start when the counter is greater
|
||||
// than zero, may happen at any time. Typically this means the calls
|
||||
// to Add should execute before the statement creating the goroutine
|
||||
// or other event to be waited for.
|
||||
func (wg *WaitGroupChan) Add(delta int) {
|
||||
n := atomic.AddInt64(&wg.n, int64(delta))
|
||||
if n == 0 {
|
||||
close(wg.done)
|
||||
}
|
||||
}
|
||||
|
||||
// Decr decrements the WaitGroup counter by one.
|
||||
//
|
||||
// (It is like sync.WaitGroup's Done method, but we don't use Done in
|
||||
// this type, because it's ambiguous between Context.Done and
|
||||
// WaitGroup.Done. So we use DoneChan and Decr instead.)
|
||||
func (wg *WaitGroupChan) Decr() {
|
||||
wg.Add(-1)
|
||||
}
|
||||
|
||||
// Wait blocks until the WaitGroupChan counter is zero.
|
||||
func (wg *WaitGroupChan) Wait() { <-wg.done }
|
||||
|
||||
// Semaphore is a counting semaphore.
|
||||
//
|
||||
// Use NewSemaphore to create one.
|
||||
type Semaphore struct {
|
||||
c chan struct{}
|
||||
}
|
||||
|
||||
// NewSemaphore returns a semaphore with resource count n.
|
||||
func NewSemaphore(n int) Semaphore {
|
||||
return Semaphore{c: make(chan struct{}, n)}
|
||||
}
|
||||
|
||||
// Acquire blocks until a resource is acquired.
|
||||
func (s Semaphore) Acquire() {
|
||||
s.c <- struct{}{}
|
||||
}
|
||||
|
||||
// AcquireContext reports whether the resource was acquired before the ctx was done.
|
||||
func (s Semaphore) AcquireContext(ctx context.Context) bool {
|
||||
select {
|
||||
case s.c <- struct{}{}:
|
||||
return true
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TryAcquire reports, without blocking, whether the resource was acquired.
|
||||
func (s Semaphore) TryAcquire() bool {
|
||||
select {
|
||||
case s.c <- struct{}{}:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Release releases a resource.
|
||||
func (s Semaphore) Release() {
|
||||
<-s.c
|
||||
}
|
||||
|
||||
// Map is a Go map protected by a [sync.RWMutex].
|
||||
// It is preferred over [sync.Map] for maps with entries that change
|
||||
// at a relatively high frequency.
|
||||
// This must not be shallow copied.
|
||||
type Map[K comparable, V any] struct {
|
||||
mu sync.RWMutex
|
||||
m map[K]V
|
||||
}
|
||||
|
||||
// Load loads the value for the provided key and whether it was found.
|
||||
func (m *Map[K, V]) Load(key K) (value V, loaded bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
value, loaded = m.m[key]
|
||||
return value, loaded
|
||||
}
|
||||
|
||||
// LoadFunc calls f with the value for the provided key
|
||||
// regardless of whether the entry exists or not.
|
||||
// The lock is held for the duration of the call to f.
|
||||
func (m *Map[K, V]) LoadFunc(key K, f func(value V, loaded bool)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
value, loaded := m.m[key]
|
||||
f(value, loaded)
|
||||
}
|
||||
|
||||
// Store stores the value for the provided key.
|
||||
func (m *Map[K, V]) Store(key K, value V) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
mak.Set(&m.m, key, value)
|
||||
}
|
||||
|
||||
// LoadOrStore returns the value for the given key if it exists
|
||||
// otherwise it stores value.
|
||||
func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
|
||||
if actual, loaded = m.Load(key); loaded {
|
||||
return actual, loaded
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
actual, loaded = m.m[key]
|
||||
if !loaded {
|
||||
actual = value
|
||||
mak.Set(&m.m, key, value)
|
||||
}
|
||||
return actual, loaded
|
||||
}
|
||||
|
||||
// LoadOrInit returns the value for the given key if it exists
|
||||
// otherwise f is called to construct the value to be set.
|
||||
// The lock is held for the duration to prevent duplicate initialization.
|
||||
func (m *Map[K, V]) LoadOrInit(key K, f func() V) (actual V, loaded bool) {
|
||||
if actual, loaded := m.Load(key); loaded {
|
||||
return actual, loaded
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if actual, loaded = m.m[key]; loaded {
|
||||
return actual, loaded
|
||||
}
|
||||
|
||||
loaded = false
|
||||
actual = f()
|
||||
mak.Set(&m.m, key, actual)
|
||||
return actual, loaded
|
||||
}
|
||||
|
||||
// LoadAndDelete returns the value for the given key if it exists.
|
||||
// It ensures that the map is cleared of any entry for the key.
|
||||
func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
value, loaded = m.m[key]
|
||||
if loaded {
|
||||
delete(m.m, key)
|
||||
}
|
||||
return value, loaded
|
||||
}
|
||||
|
||||
// Delete deletes the entry identified by key.
|
||||
func (m *Map[K, V]) Delete(key K) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.m, key)
|
||||
}
|
||||
|
||||
// Keys iterates over all keys in the map in an undefined order.
|
||||
// A read lock is held for the entire duration of the iteration.
|
||||
// Use the [WithLock] method instead to mutate the map during iteration.
|
||||
func (m *Map[K, V]) Keys() iter.Seq[K] {
|
||||
return func(yield func(K) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k := range m.m {
|
||||
if !yield(k) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Values iterates over all values in the map in an undefined order.
|
||||
// A read lock is held for the entire duration of the iteration.
|
||||
// Use the [WithLock] method instead to mutate the map during iteration.
|
||||
func (m *Map[K, V]) Values() iter.Seq[V] {
|
||||
return func(yield func(V) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for _, v := range m.m {
|
||||
if !yield(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All iterates over all entries in the map in an undefined order.
|
||||
// A read lock is held for the entire duration of the iteration.
|
||||
// Use the [WithLock] method instead to mutate the map during iteration.
|
||||
func (m *Map[K, V]) All() iter.Seq2[K, V] {
|
||||
return func(yield func(K, V) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.m {
|
||||
if !yield(k, v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithLock calls f with the underlying map.
|
||||
// Use of m2 must not escape the duration of this call.
|
||||
// The write-lock is held for the entire duration of this call.
|
||||
func (m *Map[K, V]) WithLock(f func(m2 map[K]V)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.m == nil {
|
||||
m.m = make(map[K]V)
|
||||
}
|
||||
f(m.m)
|
||||
}
|
||||
|
||||
// Len returns the length of the map.
|
||||
func (m *Map[K, V]) Len() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return len(m.m)
|
||||
}
|
||||
|
||||
// Clear removes all entries from the map.
|
||||
func (m *Map[K, V]) Clear() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
clear(m.m)
|
||||
}
|
||||
|
||||
// Swap stores the value for the provided key, and returns the previous value
|
||||
// (if any). If there was no previous value set, a zero value will be returned.
|
||||
func (m *Map[K, V]) Swap(key K, value V) (oldValue V) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
oldValue = m.m[key]
|
||||
mak.Set(&m.m, key, value)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
// WaitGroup is identical to [sync.WaitGroup],
|
||||
// but provides a Go method to start a goroutine.
|
||||
type WaitGroup struct{ sync.WaitGroup }
|
||||
|
||||
// Go calls the given function in a new goroutine.
|
||||
// It automatically increments the counter before execution and
|
||||
// automatically decrements the counter after execution.
|
||||
// It must not be called concurrently with Wait.
|
||||
func (wg *WaitGroup) Go(f func()) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
f()
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user