blob: dc0faf6a604d96f3278fb18b45d39296115545b0 [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
Péter Szabó12206f62009-11-30 12:10:56 -080038// RLock locks rw for reading.
Brad Fitzpatricke2160cc2017-06-14 06:16:49 +000039//
40// It should not be used for recursive read locking; a blocked Lock
41// call excludes new readers from acquiring the lock. See the
42// documentation on the RWMutex type.
Péter Szabó12206f62009-11-30 12:10:56 -080043func (rw *RWMutex) RLock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020044 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +020045 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020046 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040047 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -070048 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
49 // A writer is pending, wait for it.
Carlo Alberto Ferraris41cb0ae2018-11-09 22:49:38 +090050 runtime_SemacquireMutex(&rw.readerSem, false, 0)
Péter Szabó12206f62009-11-30 12:10:56 -080051 }
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020052 if race.Enabled {
53 race.Enable()
54 race.Acquire(unsafe.Pointer(&rw.readerSem))
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040055 }
Péter Szabó12206f62009-11-30 12:10:56 -080056}
57
58// RUnlock undoes a single RLock call;
59// it does not affect other simultaneous readers.
60// It is a run-time error if rw is not locked for reading
61// on entry to RUnlock.
62func (rw *RWMutex) RUnlock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020063 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +020064 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020065 race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
66 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040067 }
Dmitriy Vyukov22d46d52014-06-19 22:19:56 -070068 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
Carlo Alberto Ferraris05051b52018-11-13 17:08:17 +090069 // Outlined slow-path to allow the fast-path to be inlined
70 rw.rUnlockSlow(r)
Péter Szabó12206f62009-11-30 12:10:56 -080071 }
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020072 if race.Enabled {
73 race.Enable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040074 }
Péter Szabó12206f62009-11-30 12:10:56 -080075}
76
Carlo Alberto Ferraris05051b52018-11-13 17:08:17 +090077func (rw *RWMutex) rUnlockSlow(r int32) {
78 if r+1 == 0 || r+1 == -rwmutexMaxReaders {
79 race.Enable()
80 throw("sync: RUnlock of unlocked RWMutex")
81 }
82 // A writer is pending.
83 if atomic.AddInt32(&rw.readerWait, -1) == 0 {
84 // The last reader unblocks the writer.
85 runtime_Semrelease(&rw.writerSem, false, 1)
86 }
87}
88
Péter Szabó12206f62009-11-30 12:10:56 -080089// Lock locks rw for writing.
90// If the lock is already locked for reading or writing,
91// Lock blocks until the lock is available.
Péter Szabó12206f62009-11-30 12:10:56 -080092func (rw *RWMutex) Lock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020093 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +020094 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +020095 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +040096 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -070097 // First, resolve competition with other writers.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080098 rw.w.Lock()
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -070099 // Announce to readers there is a pending writer.
100 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
101 // Wait for active readers.
102 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
Carlo Alberto Ferraris41cb0ae2018-11-09 22:49:38 +0900103 runtime_SemacquireMutex(&rw.writerSem, false, 0)
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700104 }
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200105 if race.Enabled {
106 race.Enable()
107 race.Acquire(unsafe.Pointer(&rw.readerSem))
108 race.Acquire(unsafe.Pointer(&rw.writerSem))
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400109 }
Péter Szabó12206f62009-11-30 12:10:56 -0800110}
111
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +0000112// Unlock unlocks rw for writing. It is a run-time error if rw is
Rob Pike7c189a82011-02-01 21:29:46 -0800113// not locked for writing on entry to Unlock.
Péter Szabó12206f62009-11-30 12:10:56 -0800114//
Rob Pike7c189a82011-02-01 21:29:46 -0800115// As with Mutexes, a locked RWMutex is not associated with a particular
Brad Fitzpatrick165e7522018-01-16 22:57:44 +0000116// goroutine. One goroutine may RLock (Lock) a RWMutex and then
Péter Szabó12206f62009-11-30 12:10:56 -0800117// arrange for another goroutine to RUnlock (Unlock) it.
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700118func (rw *RWMutex) Unlock() {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200119 if race.Enabled {
Rémy Oudompheng5bb3a662013-04-08 23:46:54 +0200120 _ = rw.w.state
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200121 race.Release(unsafe.Pointer(&rw.readerSem))
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200122 race.Disable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400123 }
124
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700125 // Announce to readers there is no active writer.
126 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
Dmitriy Vyukov22d46d52014-06-19 22:19:56 -0700127 if r >= rwmutexMaxReaders {
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200128 race.Enable()
Russ Cox40d81cf2016-10-18 10:26:07 -0400129 throw("sync: Unlock of unlocked RWMutex")
Dmitriy Vyukov22d46d52014-06-19 22:19:56 -0700130 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700131 // Unblock blocked readers, if any.
132 for i := 0; i < int(r); i++ {
Carlo Alberto Ferraris4c3f2602018-11-10 08:28:44 +0900133 runtime_Semrelease(&rw.readerSem, false, 0)
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700134 }
135 // Allow other writers to proceed.
136 rw.w.Unlock()
Dmitry Vyukov7b767f42015-09-23 10:03:54 +0200137 if race.Enabled {
138 race.Enable()
Dmitriy Vyukov53390c82012-10-07 22:07:03 +0400139 }
Dmitriy Vyukovdaaf29c2011-07-12 09:24:21 -0700140}
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -0500141
142// RLocker returns a Locker interface that implements
143// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
144func (rw *RWMutex) RLocker() Locker {
145 return (*rlocker)(rw)
146}
147
148type rlocker RWMutex
149
150func (r *rlocker) Lock() { (*RWMutex)(r).RLock() }
151func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }