|
|
@@ -11,6 +11,9 @@ import (
|
|
|
"io"
|
|
|
"slices"
|
|
|
"strings"
|
|
|
+ "sync"
|
|
|
+
|
|
|
+ "tailscale.com/syncs"
|
|
|
)
|
|
|
|
|
|
// Set is a string-to-Var map variable that satisfies the expvar.Var
|
|
|
@@ -37,6 +40,8 @@ type Set struct {
|
|
|
type LabelMap struct {
|
|
|
Label string
|
|
|
expvar.Map
|
|
|
+ // shardedIntMu orders the initialization of new shardedint keys
|
|
|
+ shardedIntMu sync.Mutex
|
|
|
}
|
|
|
|
|
|
// SetInt64 sets the *Int value stored under the given map key.
|
|
|
@@ -44,6 +49,19 @@ func (m *LabelMap) SetInt64(key string, v int64) {
|
|
|
m.Get(key).Set(v)
|
|
|
}
|
|
|
|
|
|
+// Add adds delta to the any int-like value stored under the given map key.
|
|
|
+func (m *LabelMap) Add(key string, delta int64) {
|
|
|
+ type intAdder interface {
|
|
|
+ Add(delta int64)
|
|
|
+ }
|
|
|
+ o := m.Map.Get(key)
|
|
|
+ if o == nil {
|
|
|
+ m.Map.Add(key, delta)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.(intAdder).Add(delta)
|
|
|
+}
|
|
|
+
|
|
|
// Get returns a direct pointer to the expvar.Int for key, creating it
|
|
|
// if necessary.
|
|
|
func (m *LabelMap) Get(key string) *expvar.Int {
|
|
|
@@ -51,6 +69,23 @@ func (m *LabelMap) Get(key string) *expvar.Int {
|
|
|
return m.Map.Get(key).(*expvar.Int)
|
|
|
}
|
|
|
|
|
|
+// GetShardedInt returns a direct pointer to the syncs.ShardedInt for key,
|
|
|
+// creating it if necessary.
|
|
|
+func (m *LabelMap) GetShardedInt(key string) *syncs.ShardedInt {
|
|
|
+ i := m.Map.Get(key)
|
|
|
+ if i == nil {
|
|
|
+ m.shardedIntMu.Lock()
|
|
|
+ defer m.shardedIntMu.Unlock()
|
|
|
+ i = m.Map.Get(key)
|
|
|
+ if i != nil {
|
|
|
+ return i.(*syncs.ShardedInt)
|
|
|
+ }
|
|
|
+ i = syncs.NewShardedInt()
|
|
|
+ m.Set(key, i)
|
|
|
+ }
|
|
|
+ return i.(*syncs.ShardedInt)
|
|
|
+}
|
|
|
+
|
|
|
// GetIncrFunc returns a function that increments the expvar.Int named by key.
|
|
|
//
|
|
|
// Most callers should not need this; it exists to satisfy an
|