Skip to content

Commit 645d078

Browse files
committed
sync: add Mutex.TryLock, RWMutex.TryLock, RWMutex.TryRLock
Use of these functions is almost (but not) always a bad idea. Very rarely they are necessary, and third-party implementations (using a mutex and an atomic word, say) cannot integrate as well with the race detector as implmentations in package sync itself. Fixes #45435. Change-Id: I0128ca48ef5e0a3b09c913f0f3a7ee5c56388000 Reviewed-on: https://go-review.googlesource.com/c/go/+/319769 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent 3aecb3a commit 645d078

File tree

4 files changed

+120
-0
lines changed

4 files changed

+120
-0
lines changed

src/sync/mutex.go

+15
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ func (m *Mutex) Lock() {
8181
m.lockSlow()
8282
}
8383

84+
// TryLock tries to lock m and reports whether it succeeded.
85+
//
86+
// Note that while correct uses of TryLock do exist, they are rare,
87+
// and use of TryLock is often a sign of a deeper problem
88+
// in a particular use of mutexes.
89+
func (m *Mutex) TryLock() bool {
90+
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
91+
if race.Enabled {
92+
race.Acquire(unsafe.Pointer(m))
93+
}
94+
return true
95+
}
96+
return false
97+
}
98+
8499
func (m *Mutex) lockSlow() {
85100
var waitStartTime int64
86101
starving := false

src/sync/mutex_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ func BenchmarkContendedSemaphore(b *testing.B) {
6060

6161
func HammerMutex(m *Mutex, loops int, cdone chan bool) {
6262
for i := 0; i < loops; i++ {
63+
if i%3 == 0 {
64+
if m.TryLock() {
65+
m.Unlock()
66+
}
67+
continue
68+
}
6369
m.Lock()
6470
m.Unlock()
6571
}
@@ -71,7 +77,19 @@ func TestMutex(t *testing.T) {
7177
t.Logf("got mutexrate %d expected 0", n)
7278
}
7379
defer runtime.SetMutexProfileFraction(0)
80+
7481
m := new(Mutex)
82+
83+
m.Lock()
84+
if m.TryLock() {
85+
t.Fatalf("TryLock succeeded with mutex locked")
86+
}
87+
m.Unlock()
88+
if !m.TryLock() {
89+
t.Fatalf("TryLock failed with mutex unlocked")
90+
}
91+
m.Unlock()
92+
7593
c := make(chan bool)
7694
for i := 0; i < 10; i++ {
7795
go HammerMutex(m, 1000, c)

src/sync/rwmutex.go

+59
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,34 @@ func (rw *RWMutex) RLock() {
6868
}
6969
}
7070

71+
// TryRLock tries to lock rw for reading and reports whether it succeeded.
72+
//
73+
// Note that while correct uses of TryRLock do exist, they are rare,
74+
// and use of TryRLock is often a sign of a deeper problem
75+
// in a particular use of mutexes.
76+
func (rw *RWMutex) TryRLock() bool {
77+
if race.Enabled {
78+
_ = rw.w.state
79+
race.Disable()
80+
}
81+
for {
82+
c := atomic.LoadInt32(&rw.readerCount)
83+
if c < 0 {
84+
if race.Enabled {
85+
race.Enable()
86+
}
87+
return false
88+
}
89+
if atomic.CompareAndSwapInt32(&rw.readerCount, c, c+1) {
90+
if race.Enabled {
91+
race.Enable()
92+
race.Acquire(unsafe.Pointer(&rw.readerSem))
93+
}
94+
return true
95+
}
96+
}
97+
}
98+
7199
// RUnlock undoes a single RLock call;
72100
// it does not affect other simultaneous readers.
73101
// It is a run-time error if rw is not locked for reading
@@ -122,6 +150,37 @@ func (rw *RWMutex) Lock() {
122150
}
123151
}
124152

153+
// TryLock tries to lock rw for writing and reports whether it succeeded.
154+
//
155+
// Note that while correct uses of TryLock do exist, they are rare,
156+
// and use of TryLock is often a sign of a deeper problem
157+
// in a particular use of mutexes.
158+
func (rw *RWMutex) TryLock() bool {
159+
if race.Enabled {
160+
_ = rw.w.state
161+
race.Disable()
162+
}
163+
if !rw.w.TryLock() {
164+
if race.Enabled {
165+
race.Enable()
166+
}
167+
return false
168+
}
169+
if !atomic.CompareAndSwapInt32(&rw.readerCount, 0, -rwmutexMaxReaders) {
170+
rw.w.Unlock()
171+
if race.Enabled {
172+
race.Enable()
173+
}
174+
return false
175+
}
176+
if race.Enabled {
177+
race.Enable()
178+
race.Acquire(unsafe.Pointer(&rw.readerSem))
179+
race.Acquire(unsafe.Pointer(&rw.writerSem))
180+
}
181+
return true
182+
}
183+
125184
// Unlock unlocks rw for writing. It is a run-time error if rw is
126185
// not locked for writing on entry to Unlock.
127186
//

src/sync/rwmutex_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,34 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
108108
}
109109

110110
func TestRWMutex(t *testing.T) {
111+
var m RWMutex
112+
113+
m.Lock()
114+
if m.TryLock() {
115+
t.Fatalf("TryLock succeeded with mutex locked")
116+
}
117+
if m.TryRLock() {
118+
t.Fatalf("TryRLock succeeded with mutex locked")
119+
}
120+
m.Unlock()
121+
122+
if !m.TryLock() {
123+
t.Fatalf("TryLock failed with mutex unlocked")
124+
}
125+
m.Unlock()
126+
127+
if !m.TryRLock() {
128+
t.Fatalf("TryRLock failed with mutex unlocked")
129+
}
130+
if !m.TryRLock() {
131+
t.Fatalf("TryRLock failed with mutex rlocked")
132+
}
133+
if m.TryLock() {
134+
t.Fatalf("TryLock succeeded with mutex rlocked")
135+
}
136+
m.RUnlock()
137+
m.RUnlock()
138+
111139
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
112140
n := 1000
113141
if testing.Short() {

0 commit comments

Comments
 (0)