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_Semacquire(&rw.readerSem)
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		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
70			race.Enable()
71			throw("sync: RUnlock of unlocked RWMutex")
72		}
73		// A writer is pending.
74		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
75			// The last reader unblocks the writer.
76			runtime_Semrelease(&rw.writerSem, false)
77		}
78	}
79	if race.Enabled {
80		race.Enable()
81	}
82}
83
84// Lock locks rw for writing.
85// If the lock is already locked for reading or writing,
86// Lock blocks until the lock is available.
87func (rw *RWMutex) Lock() {
88	if race.Enabled {
89		_ = rw.w.state
90		race.Disable()
91	}
92	// First, resolve competition with other writers.
93	rw.w.Lock()
94	// Announce to readers there is a pending writer.
95	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
96	// Wait for active readers.
97	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
98		runtime_Semacquire(&rw.writerSem)
99	}
100	if race.Enabled {
101		race.Enable()
102		race.Acquire(unsafe.Pointer(&rw.readerSem))
103		race.Acquire(unsafe.Pointer(&rw.writerSem))
104	}
105}
106
107// Unlock unlocks rw for writing. It is a run-time error if rw is
108// not locked for writing on entry to Unlock.
109//
110// As with Mutexes, a locked RWMutex is not associated with a particular
111// goroutine. One goroutine may RLock (Lock) a RWMutex and then
112// arrange for another goroutine to RUnlock (Unlock) it.
113func (rw *RWMutex) Unlock() {
114	if race.Enabled {
115		_ = rw.w.state
116		race.Release(unsafe.Pointer(&rw.readerSem))
117		race.Release(unsafe.Pointer(&rw.writerSem))
118		race.Disable()
119	}
120
121	// Announce to readers there is no active writer.
122	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
123	if r >= rwmutexMaxReaders {
124		race.Enable()
125		throw("sync: Unlock of unlocked RWMutex")
126	}
127	// Unblock blocked readers, if any.
128	for i := 0; i < int(r); i++ {
129		runtime_Semrelease(&rw.readerSem, false)
130	}
131	// Allow other writers to proceed.
132	rw.w.Unlock()
133	if race.Enabled {
134		race.Enable()
135	}
136}
137
138// RLocker returns a Locker interface that implements
139// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
140func (rw *RWMutex) RLocker() Locker {
141	return (*rlocker)(rw)
142}
143
144type rlocker RWMutex
145
146func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
147func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }
148