aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/rwmutex.go
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2017-07-06 11:55:39 -0400
committerAustin Clements <austin@google.com>2017-07-06 17:04:25 +0000
commitf3b5a2bc1983ddb83d72e741b176993d9b800faf (patch)
treeeecbf8b578dc74f1db946744c7e3426d70f3751e /src/runtime/rwmutex.go
parentbb3be403e79731b208c41bd170a6a87642d988da (diff)
downloadgo-f3b5a2bc1983ddb83d72e741b176993d9b800faf.tar.gz
go-f3b5a2bc1983ddb83d72e741b176993d9b800faf.zip
runtime: prevent descheduling while holding rwmutex read lock
Currently only the rwmutex write lock prevents descheduling. The read lock does not. This leads to the following situation: 1. A reader acquires the lock and gets descheduled. 2. GOMAXPROCS writers attempt to acquire the lock (or at least one writer does, followed by readers). This blocks all of the Ps. 3. There is no 3. The descheduled reader never gets to run again because there are no Ps, so it never releases the lock and the system deadlocks. Fix this by preventing descheduling while holding the read lock. This requires also rewriting TestParallelRWMutexReaders to always create enough GOMAXPROCS and to use non-blocking operations for synchronization. Fixes #20903. Change-Id: Ibd460663a7e5a555be5490e13b2eaaa295fac39f Reviewed-on: https://go-review.googlesource.com/47632 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/runtime/rwmutex.go')
-rw-r--r--src/runtime/rwmutex.go8
1 files changed, 7 insertions, 1 deletions
diff --git a/src/runtime/rwmutex.go b/src/runtime/rwmutex.go
index bca29d15d0..7eeb559adb 100644
--- a/src/runtime/rwmutex.go
+++ b/src/runtime/rwmutex.go
@@ -31,6 +31,11 @@ const rwmutexMaxReaders = 1 << 30
// rlock locks rw for reading.
func (rw *rwmutex) rlock() {
+ // The reader must not be allowed to lose its P or else other
+ // things blocking on the lock may consume all of the Ps and
+ // deadlock (issue #20903). Alternatively, we could drop the P
+ // while sleeping.
+ acquirem()
if int32(atomic.Xadd(&rw.readerCount, 1)) < 0 {
// A writer is pending. Park on the reader queue.
systemstack(func() {
@@ -70,11 +75,12 @@ func (rw *rwmutex) runlock() {
unlock(&rw.rLock)
}
}
+ releasem(getg().m)
}
// lock locks rw for writing.
func (rw *rwmutex) lock() {
- // Resolve competition with other writers.
+ // Resolve competition with other writers and stick to our P.
lock(&rw.wLock)
m := getg().m
// Announce that there is a pending writer.