summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2024-07-13 14:34:17 -0700
committerJoe Tsai <joetsai@digital-static.net>2024-07-13 14:40:04 -0700
commit205720530036c12c913e4b3ddf8342879d32faf5 (patch)
tree62f3427e26d1bbe2caa887a4b72acda8d9556c0b
parentc8f258a90427a80db831c3791742488fc8f9032a (diff)
downloadtailscale-dsnet/syncs-lock.tar.xz
tailscale-dsnet/syncs-lock.zip
syncs: add LockFunc, LockValue, LockValues, and Mutexdsnet/syncs-lock
The first 3 functions are helpers for running functions under the protection of a lock. The Mutex type is a wrapper over sync.Mutex with a Do method that runs a function under the protection of a lock. Updates #11038 Updates #cleanup Signed-off-by: Joe Tsai <joetsai@digital-static.net>
-rw-r--r--syncs/lock_example_test.go37
-rw-r--r--syncs/syncs.go36
2 files changed, 73 insertions, 0 deletions
diff --git a/syncs/lock_example_test.go b/syncs/lock_example_test.go
new file mode 100644
index 000000000..13184c7c0
--- /dev/null
+++ b/syncs/lock_example_test.go
@@ -0,0 +1,37 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+package syncs_test
+
+import (
+ "encoding/hex"
+ "log"
+ "sync"
+
+ "tailscale.com/syncs"
+)
+
+func ExampleLockFunc() {
+ var nodesMu sync.Mutex
+ var nodes []string
+ syncs.LockFunc(&nodesMu, func() { nodes = append(nodes, "node123") })
+}
+
+func ExampleLockValue() {
+ var nodesMu sync.Mutex
+ var nodes []string
+ n := syncs.LockValue(&nodesMu, func() int { return len(nodes) })
+ log.Printf("there are %d nodes", n)
+}
+
+func ExampleLockValues() {
+ var bufferMu sync.Mutex
+ var buffer string
+ b, err := syncs.LockValues(&bufferMu, func() ([]byte, error) {
+ return hex.DecodeString(buffer)
+ })
+ if err != nil {
+ log.Fatalf("Decode error: %v", err)
+ }
+ log.Printf("decoded %d bytes", len(b))
+}
diff --git a/syncs/syncs.go b/syncs/syncs.go
index c3f729a90..a32947c97 100644
--- a/syncs/syncs.go
+++ b/syncs/syncs.go
@@ -304,3 +304,39 @@ func (wg *WaitGroup) Go(f func()) {
f()
}()
}
+
+// TODO(https://go.dev/issue/63941): LockFunc, LockValue, and LockValues
+// are helper functions proposed upstream. Their naming and signature
+// are based on the existing OnceFunc, OnceValue, and OnceValues
+// helper functions already in the [sync] package.
+
+// LockFunc runs f while holding the lock.
+func LockFunc(lock sync.Locker, f func()) {
+ lock.Lock()
+ defer lock.Unlock()
+ f()
+}
+
+// LockValue runs f while holding the lock and returns the argument.
+func LockValue[T any](lock sync.Locker, f func() T) T {
+ lock.Lock()
+ defer lock.Unlock()
+ return f()
+}
+
+// LockValues runs f while holding the lock and returns the arguments.
+func LockValues[T1, T2 any](lock sync.Locker, f func() (T1, T2)) (T1, T2) {
+ lock.Lock()
+ defer lock.Unlock()
+ return f()
+}
+
+// Mutex is identical to [sync.Mutex], but with additional methods.
+type Mutex struct{ sync.Mutex }
+
+// Do runs f while holding the lock.
+func (m *Mutex) Do(f func()) {
+ m.Lock()
+ defer m.Unlock()
+ f()
+}