syncs: add LockFunc, LockValue, LockValues, and Mutex

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>
This commit is contained in:
Joe Tsai 2024-07-13 14:34:17 -07:00
parent c8f258a904
commit 2057205300
2 changed files with 73 additions and 0 deletions

View File

@ -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))
}

View File

@ -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()
}