Update dependencies
This commit is contained in:
69
vendor/tailscale.com/syncs/shardedint.go
generated
vendored
Normal file
69
vendor/tailscale.com/syncs/shardedint.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// ShardedInt provides a sharded atomic int64 value that optimizes high
|
||||
// frequency (Mhz range and above) writes in highly parallel workloads.
|
||||
// The zero value is not safe for use; use [NewShardedInt].
|
||||
// ShardedInt implements the expvar.Var interface.
|
||||
type ShardedInt struct {
|
||||
sv *ShardValue[intShard]
|
||||
}
|
||||
|
||||
// NewShardedInt returns a new [ShardedInt].
|
||||
func NewShardedInt() *ShardedInt {
|
||||
return &ShardedInt{
|
||||
sv: NewShardValue[intShard](),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds delta to the value.
|
||||
func (m *ShardedInt) Add(delta int64) {
|
||||
m.sv.One(func(v *intShard) {
|
||||
v.Add(delta)
|
||||
})
|
||||
}
|
||||
|
||||
type intShard struct {
|
||||
atomic.Int64
|
||||
_ cpu.CacheLinePad // avoid false sharing of neighboring shards
|
||||
}
|
||||
|
||||
// Value returns the current value.
|
||||
func (m *ShardedInt) Value() int64 {
|
||||
var v int64
|
||||
for s := range m.sv.All {
|
||||
v += s.Load()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetDistribution returns the current value in each shard.
|
||||
// This is intended for observability/debugging only.
|
||||
func (m *ShardedInt) GetDistribution() []int64 {
|
||||
v := make([]int64, 0, m.sv.Len())
|
||||
for s := range m.sv.All {
|
||||
v = append(v, s.Load())
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// String implements the expvar.Var interface
|
||||
func (m *ShardedInt) String() string {
|
||||
v, _ := json.Marshal(m.Value())
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// AppendText implements the encoding.TextAppender interface
|
||||
func (m *ShardedInt) AppendText(b []byte) ([]byte, error) {
|
||||
return strconv.AppendInt(b, m.Value(), 10), nil
|
||||
}
|
||||
36
vendor/tailscale.com/syncs/shardvalue.go
generated
vendored
Normal file
36
vendor/tailscale.com/syncs/shardvalue.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package syncs
|
||||
|
||||
// TODO(raggi): this implementation is still imperfect as it will still result
|
||||
// in cross CPU sharing periodically, we instead really want a per-CPU shard
|
||||
// key, but the limitations of calling platform code make reaching for even the
|
||||
// getcpu vdso very painful. See https://github.com/golang/go/issues/18802, and
|
||||
// hopefully one day we can replace with a primitive that falls out of that
|
||||
// work.
|
||||
|
||||
// ShardValue contains a value sharded over a set of shards.
|
||||
// In order to be useful, T should be aligned to cache lines.
|
||||
// Users must organize that usage in One and All is concurrency safe.
|
||||
// The zero value is not safe for use; use [NewShardValue].
|
||||
type ShardValue[T any] struct {
|
||||
shards []T
|
||||
|
||||
//lint:ignore U1000 unused under tailscale_go builds.
|
||||
pool shardValuePool
|
||||
}
|
||||
|
||||
// Len returns the number of shards.
|
||||
func (sp *ShardValue[T]) Len() int {
|
||||
return len(sp.shards)
|
||||
}
|
||||
|
||||
// All yields a pointer to the value in each shard.
|
||||
func (sp *ShardValue[T]) All(yield func(*T) bool) {
|
||||
for i := range sp.shards {
|
||||
if !yield(&sp.shards[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
36
vendor/tailscale.com/syncs/shardvalue_go.go
generated
vendored
Normal file
36
vendor/tailscale.com/syncs/shardvalue_go.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !tailscale_go
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type shardValuePool struct {
|
||||
atomic.Int64
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
// NewShardValue constructs a new ShardValue[T] with a shard per CPU.
|
||||
func NewShardValue[T any]() *ShardValue[T] {
|
||||
sp := &ShardValue[T]{
|
||||
shards: make([]T, runtime.NumCPU()),
|
||||
}
|
||||
sp.pool.New = func() any {
|
||||
i := sp.pool.Add(1) - 1
|
||||
return &sp.shards[i%int64(len(sp.shards))]
|
||||
}
|
||||
return sp
|
||||
}
|
||||
|
||||
// One yields a pointer to a single shard value with best-effort P-locality.
|
||||
func (sp *ShardValue[T]) One(yield func(*T)) {
|
||||
v := sp.pool.Get().(*T)
|
||||
yield(v)
|
||||
sp.pool.Put(v)
|
||||
}
|
||||
24
vendor/tailscale.com/syncs/shardvalue_tailscale.go
generated
vendored
Normal file
24
vendor/tailscale.com/syncs/shardvalue_tailscale.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// TODO(raggi): update build tag after toolchain update
|
||||
//go:build tailscale_go
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
//lint:ignore U1000 unused under tailscale_go builds.
|
||||
type shardValuePool struct{}
|
||||
|
||||
// NewShardValue constructs a new ShardValue[T] with a shard per CPU.
|
||||
func NewShardValue[T any]() *ShardValue[T] {
|
||||
return &ShardValue[T]{shards: make([]T, runtime.NumCPU())}
|
||||
}
|
||||
|
||||
// One yields a pointer to a single shard value with best-effort P-locality.
|
||||
func (sp *ShardValue[T]) One(f func(*T)) {
|
||||
f(&sp.shards[runtime.TailscaleCurrentP()%len(sp.shards)])
|
||||
}
|
||||
62
vendor/tailscale.com/syncs/syncs.go
generated
vendored
62
vendor/tailscale.com/syncs/syncs.go
generated
vendored
@@ -25,6 +25,7 @@ func initClosedChan() <-chan struct{} {
|
||||
}
|
||||
|
||||
// AtomicValue is the generic version of [atomic.Value].
|
||||
// See [MutexValue] for guidance on whether to use this type.
|
||||
type AtomicValue[T any] struct {
|
||||
v atomic.Value
|
||||
}
|
||||
@@ -74,6 +75,67 @@ func (v *AtomicValue[T]) CompareAndSwap(oldV, newV T) (swapped bool) {
|
||||
return v.v.CompareAndSwap(wrappedValue[T]{oldV}, wrappedValue[T]{newV})
|
||||
}
|
||||
|
||||
// MutexValue is a value protected by a mutex.
|
||||
//
|
||||
// AtomicValue, [MutexValue], [atomic.Pointer] are similar and
|
||||
// overlap in their use cases.
|
||||
//
|
||||
// - Use [atomic.Pointer] if the value being stored is a pointer and
|
||||
// you only ever need load and store operations.
|
||||
// An atomic pointer only occupies 1 word of memory.
|
||||
//
|
||||
// - Use [MutexValue] if the value being stored is not a pointer or
|
||||
// you need the ability for a mutex to protect a set of operations
|
||||
// performed on the value.
|
||||
// A mutex-guarded value occupies 1 word of memory plus
|
||||
// the memory representation of T.
|
||||
//
|
||||
// - AtomicValue is useful for non-pointer types that happen to
|
||||
// have the memory layout of a single pointer.
|
||||
// Examples include a map, channel, func, or a single field struct
|
||||
// that contains any prior types.
|
||||
// An atomic value occupies 2 words of memory.
|
||||
// Consequently, Storing of non-pointer types always allocates.
|
||||
//
|
||||
// Note that [AtomicValue] has the ability to report whether it was set
|
||||
// while [MutexValue] lacks the ability to detect if the value was set
|
||||
// and it happens to be the zero value of T. If such a use case is
|
||||
// necessary, then you could consider wrapping T in [opt.Value].
|
||||
type MutexValue[T any] struct {
|
||||
mu sync.Mutex
|
||||
v T
|
||||
}
|
||||
|
||||
// WithLock calls f with a pointer to the value while holding the lock.
|
||||
// The provided pointer must not leak beyond the scope of the call.
|
||||
func (m *MutexValue[T]) WithLock(f func(p *T)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(&m.v)
|
||||
}
|
||||
|
||||
// Load returns a shallow copy of the underlying value.
|
||||
func (m *MutexValue[T]) Load() T {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.v
|
||||
}
|
||||
|
||||
// Store stores a shallow copy of the provided value.
|
||||
func (m *MutexValue[T]) Store(v T) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.v = v
|
||||
}
|
||||
|
||||
// Swap stores new into m and returns the previous value.
|
||||
func (m *MutexValue[T]) Swap(new T) (old T) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
old, m.v = m.v, new
|
||||
return old
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
Reference in New Issue
Block a user