summaryrefslogtreecommitdiffhomepage
path: root/syncs
diff options
context:
space:
mode:
authorJosh Bleecher Snyder <josh@tailscale.com>2022-03-16 17:02:16 -0700
committerJosh Bleecher Snyder <josharian@gmail.com>2022-03-17 10:57:41 -0700
commit997b19545bbf8e5f548eaa87df2f8b87c77e9b4b (patch)
tree26e130a347d81400baca999aba814fd518fc3c09 /syncs
parentead16b24ec64ef1c82956011f119c500ae360d07 (diff)
downloadtailscale-997b19545bbf8e5f548eaa87df2f8b87c77e9b4b.tar.xz
tailscale-997b19545bbf8e5f548eaa87df2f8b87c77e9b4b.zip
syncs: use TryLock and TryRLock instead of unsafe
The docs say: Note that while correct uses of TryLock do exist, they are rare, and use of TryLock is often a sign of a deeper problem in a particular use of mutexes. Rare code! Or bad code! Who can tell! Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
Diffstat (limited to 'syncs')
-rw-r--r--syncs/locked.go44
1 files changed, 9 insertions, 35 deletions
diff --git a/syncs/locked.go b/syncs/locked.go
index f4498a800..e7b5ec311 100644
--- a/syncs/locked.go
+++ b/syncs/locked.go
@@ -2,58 +2,32 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.13 && !go1.19
-// +build go1.13,!go1.19
-
-// This file makes assumptions about the inner workings of sync.Mutex and sync.RWMutex.
-// This includes not just their memory layout but their invariants and functionality.
-// To prevent accidents, it is limited to a known good subset of Go versions.
-
package syncs
import (
"sync"
- "sync/atomic"
- "unsafe"
-)
-
-const (
- mutexLocked = 1
-
- // sync.Mutex field offsets
- stateOffset = 0
-
- // sync.RWMutext field offsets
- mutexOffset = 0
- readerCountOffset = 16
)
-// add returns a pointer with value p + off.
-func add(p unsafe.Pointer, off uintptr) unsafe.Pointer {
- return unsafe.Pointer(uintptr(p) + off)
-}
-
// AssertLocked panics if m is not locked.
func AssertLocked(m *sync.Mutex) {
- p := add(unsafe.Pointer(m), stateOffset)
- if atomic.LoadInt32((*int32)(p))&mutexLocked == 0 {
+ 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) {
- p := add(unsafe.Pointer(rw), readerCountOffset)
- if atomic.LoadInt32((*int32)(p)) != 0 {
- // There are readers present or writers pending, so someone has a read lock.
- return
+ if rw.TryLock() {
+ rw.Unlock()
+ panic("mutex is not locked")
}
- // No readers.
- AssertWLocked(rw)
}
// AssertWLocked panics if rw is not locked for writing.
func AssertWLocked(rw *sync.RWMutex) {
- m := (*sync.Mutex)(add(unsafe.Pointer(rw), mutexOffset))
- AssertLocked(m)
+ if rw.TryRLock() {
+ rw.RUnlock()
+ panic("mutex is not rlocked")
+ }
}