1// 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
7import (
8	"internal/race"
9	"sync/atomic"
10	"unsafe"
11)
12
13// 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
16// A RWMutex is a reader/writer mutual exclusion lock.
17// The lock can be held by an arbitrary number of readers or a single writer.
18// The zero value for a RWMutex is an unlocked mutex.
19//
20// A RWMutex must not be copied after first use.
21//
22// 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.
28type RWMutex struct {
29	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
34}
35
36const rwmutexMaxReaders = 1 << 30
37
38// RLock locks rw for reading.
39//
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.
43func (rw *RWMutex) RLock() {
44	if race.Enabled {
45		_ = rw.w.state
46		race.Disable()
47	}
48	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
49		// A writer is pending, wait for it.
50		runtime_SemacquireMutex(&rw.readerSem, false, 0)
51	}
52	if race.Enabled {
53		race.Enable()
54		race.Acquire(unsafe.Pointer(&rw.readerSem))
55	}
56}
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() {
63	if race.Enabled {
64		_ = rw.w.state
65		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
66		race.Disable()
67	}
68	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
69		// Outlined slow-path to allow the fast-path to be inlined
70		rw.rUnlockSlow(r)
71	}
72	if race.Enabled {
73		race.Enable()
74	}
75}
76
77func (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
89// Lock locks rw for writing.
90// If the lock is already locked for reading or writing,
91// Lock blocks until the lock is available.
92func (rw *RWMutex) Lock() {
93	if race.Enabled {
94		_ = rw.w.state
95		race.Disable()
96	}
97	// First, resolve competition with other writers.
98	rw.w.Lock()
99	// 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 {
103		runtime_SemacquireMutex(&rw.writerSem, false, 0)
104	}
105	if race.Enabled {
106		race.Enable()
107		race.Acquire(unsafe.Pointer(&rw.readerSem))
108		race.Acquire(unsafe.Pointer(&rw.writerSem))
109	}
110}
111
112// Unlock unlocks rw for writing. It is a run-time error if rw is
113// not locked for writing on entry to Unlock.
114//
115// As with Mutexes, a locked RWMutex is not associated with a particular
116// goroutine. One goroutine may RLock (Lock) a RWMutex and then
117// arrange for another goroutine to RUnlock (Unlock) it.
118func (rw *RWMutex) Unlock() {
119	if race.Enabled {
120		_ = rw.w.state
121		race.Release(unsafe.Pointer(&rw.readerSem))
122		race.Disable()
123	}
124
125	// Announce to readers there is no active writer.
126	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
127	if r >= rwmutexMaxReaders {
128		race.Enable()
129		throw("sync: Unlock of unlocked RWMutex")
130	}
131	// Unblock blocked readers, if any.
132	for i := 0; i < int(r); i++ {
133		runtime_Semrelease(&rw.readerSem, false, 0)
134	}
135	// Allow other writers to proceed.
136	rw.w.Unlock()
137	if race.Enabled {
138		race.Enable()
139	}
140}
141
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() }
152