blob: 3012b5548e4147d8f6495d84363bb7161c2f9779 [file] [log] [blame]
Péter Szabó12206f62009-11-30 12:10:56 -08001// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package sync
6
Dmitriy Vyukov53390c82012-10-07 22:07:03 +04007import (
Dmitry Vyukov7b767f42015-09-23 10:03:54 +02008 "internal/race"
Dmitriy Vyukov53390c82012-10-07 22:07:03 +04009 "sync/atomic"
10 "unsafe"
11)
Russ Cox12b78752011-02-25 14:29:47 -050012
Ian Lance Taylor09ebbf42017-06-15 16:42:08 -070013// There is a modified copy of this file in runtime/rwmutex.go.
14// If you make any changes here, see if you should make them there.
15
Brad Fitzpatrick165e7522018-01-16 22:57:44 +000016// A RWMutex is a reader/writer mutual exclusion lock.
Andrew Gerrandb3f98d72016-05-30 15:17:14 +100017// The lock can be held by an arbitrary number of readers or a single writer.
Joonas Kuorilehto766e1ae2017-05-24 20:37:53 +030018// The zero value for a RWMutex is an unlocked mutex.
Aliaksandr Valialkinc81a3532016-04-15 00:33:28 +030019//
Brad Fitzpatrick165e7522018-01-16 22:57:44 +000020// A RWMutex must not be copied after first use.
Andrew Gerrandb3f98d72016-05-30 15:17:14 +100021//
Brad Fitzpatricke2160cc2017-06-14 06:16:49 +000022// If a goroutine holds a RWMutex for reading and another goroutine might
23// call Lock, no goroutine should expect to be able to acquire a read lock
24// until the initial read lock is released. In particular, this prohibits
25// recursive read locking. This is to ensure that the lock eventually becomes
26// available; a blocked Lock call excludes new readers from acquiring the
27// lock.
Péter Szabó12206f62009-11-30 12:10:56 -080028type RWMutex struct {
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -070029 w Mutex // held if there are pending writers
30 writerSem uint32 // semaphore for writers to wait for completing readers
31 readerSem uint32 // semaphore for readers to wait for completing writers
32 readerCount int32 // number of pending readers
33 readerWait int32 // number of departing readers
Péter Szabó12206f62009-11-30 12:10:56 -080034}
35
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -070036const rwmutexMaxReaders = 1 << 30
37
Michael Prattb4f3d522020-11-17 16:47:08 -050038// Happens-before relationships are indicated to the race detector via:
39// - Unlock -> Lock: readerSem
40// - Unlock -> RLock: readerSem
41// - RUnlock -> Lock: writerSem
42//
43// The methods below temporarily disable handling of race synchronization
44// events in order to provide the more precise model above to the race
45// detector.
46//
47// For example, atomic.AddInt32 in RLock should not appear to provide
48// acquire-release semantics, which would incorrectly synchronize racing
49// readers, thus potentially missing races.
50
Péter Szabó12206f62009-11-30 12:10:56 -080051// RLock locks rw for reading.
Brad Fitzpatricke2160cc2017-06-14 06:16:49 +000052//
53// It should not be used for recursive read locking; a blocked Lock
54// call excludes new readers from acquiring the lock. See the
55// documentation on the RWMutex type.
Péter Szabó12206f62009-11-30 12:10:56 -080056func (rw *RWMutex) RLock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020057 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +020058 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020059 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040060 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -070061 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
62 // A writer is pending, wait for it.
Carlo Alberto Ferraris41cb0ae2018-11-09 22:49:38 +090063 runtime_SemacquireMutex(&rw.readerSem, false, 0)
Péter Szabó12206f62009-11-30 12:10:56 -080064 }
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020065 if race.Enabled {
66 race.Enable()
67 race.Acquire(unsafe.Pointer(&rw.readerSem))
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040068 }
Péter Szabó12206f62009-11-30 12:10:56 -080069}
70
71// RUnlock undoes a single RLock call;
72// it does not affect other simultaneous readers.
73// It is a run-time error if rw is not locked for reading
74// on entry to RUnlock.
75func (rw *RWMutex) RUnlock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020076 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +020077 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020078 race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
79 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040080 }
Dmitriy Vyukov22d46d52014-06-19 22:19:56 -070081 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
Carlo Alberto Ferraris05051b52018-11-13 17:08:17 +090082 // Outlined slow-path to allow the fast-path to be inlined
83 rw.rUnlockSlow(r)
Péter Szabó12206f62009-11-30 12:10:56 -080084 }
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020085 if race.Enabled {
86 race.Enable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040087 }
Péter Szabó12206f62009-11-30 12:10:56 -080088}
89
Carlo Alberto Ferraris05051b52018-11-13 17:08:17 +090090func (rw *RWMutex) rUnlockSlow(r int32) {
91 if r+1 == 0 || r+1 == -rwmutexMaxReaders {
92 race.Enable()
93 throw("sync: RUnlock of unlocked RWMutex")
94 }
95 // A writer is pending.
96 if atomic.AddInt32(&rw.readerWait, -1) == 0 {
97 // The last reader unblocks the writer.
98 runtime_Semrelease(&rw.writerSem, false, 1)
99 }
100}
101
Péter Szabó12206f62009-11-30 12:10:56 -0800102// Lock locks rw for writing.
103// If the lock is already locked for reading or writing,
104// Lock blocks until the lock is available.
Péter Szabó12206f62009-11-30 12:10:56 -0800105func (rw *RWMutex) Lock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200106 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +0200107 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200108 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400109 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700110 // First, resolve competition with other writers.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -0800111 rw.w.Lock()
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700112 // Announce to readers there is a pending writer.
113 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
114 // Wait for active readers.
115 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
Carlo Alberto Ferraris41cb0ae2018-11-09 22:49:38 +0900116 runtime_SemacquireMutex(&rw.writerSem, false, 0)
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700117 }
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200118 if race.Enabled {
119 race.Enable()
120 race.Acquire(unsafe.Pointer(&rw.readerSem))
121 race.Acquire(unsafe.Pointer(&rw.writerSem))
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400122 }
Péter Szabó12206f62009-11-30 12:10:56 -0800123}
124
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +0000125// Unlock unlocks rw for writing. It is a run-time error if rw is
Rob Pike7c189a82011-02-01 21:29:46 -0800126// not locked for writing on entry to Unlock.
Péter Szabó12206f62009-11-30 12:10:56 -0800127//
Rob Pike7c189a82011-02-01 21:29:46 -0800128// As with Mutexes, a locked RWMutex is not associated with a particular
Brad Fitzpatrick165e7522018-01-16 22:57:44 +0000129// goroutine. One goroutine may RLock (Lock) a RWMutex and then
Péter Szabó12206f62009-11-30 12:10:56 -0800130// arrange for another goroutine to RUnlock (Unlock) it.
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700131func (rw *RWMutex) Unlock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200132 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +0200133 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200134 race.Release(unsafe.Pointer(&rw.readerSem))
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200135 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400136 }
137
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700138 // Announce to readers there is no active writer.
139 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
Dmitriy Vyukov22d46d52014-06-19 22:19:56 -0700140 if r >= rwmutexMaxReaders {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200141 race.Enable()
Russ Cox40d81cf2016-10-18 10:26:07 -0400142 throw("sync: Unlock of unlocked RWMutex")
Dmitriy Vyukov22d46d52014-06-19 22:19:56 -0700143 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700144 // Unblock blocked readers, if any.
145 for i := 0; i < int(r); i++ {
Carlo Alberto Ferraris4c3f2602018-11-10 08:28:44 +0900146 runtime_Semrelease(&rw.readerSem, false, 0)
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700147 }
148 // Allow other writers to proceed.
149 rw.w.Unlock()
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200150 if race.Enabled {
151 race.Enable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400152 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700153}
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -0500154
155// RLocker returns a Locker interface that implements
156// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
157func (rw *RWMutex) RLocker() Locker {
158 return (*rlocker)(rw)
159}
160
161type rlocker RWMutex
162
163func (r *rlocker) Lock() { (*RWMutex)(r).RLock() }
164func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }