1 package sync
2 3 import (
4 "internal/task"
5 )
6 7 type Mutex = task.Mutex
8 9 //go:linkname runtimePanic runtime.runtimePanic
10 func runtimePanic(msg []byte)
11 12 type RWMutex struct {
13 // Reader count, with the number of readers that currently have read-locked
14 // this mutex.
15 // The value can be in two states: one where 0 means no readers and another
16 // where -rwMutexMaxReaders means no readers. A base of 0 is normal
17 // uncontended operation, a base of -rwMutexMaxReaders means a writer has
18 // the lock or is trying to get the lock. In the second case, readers should
19 // wait until the reader count becomes non-negative again to give the writer
20 // a chance to obtain the lock.
21 readers task.Futex
22 23 // Writer futex, normally 0. If there is a writer waiting until all readers
24 // have unlocked, this value is 1. It will be changed to a 2 (and get a
25 // wake) when the last reader unlocks.
26 writer task.Futex
27 28 // Writer lock. Held between Lock() and Unlock().
29 writerLock Mutex
30 }
31 32 const rwMutexMaxReaders = 1 << 30
33 34 // Lock locks rw for writing.
35 // If the lock is already locked for reading or writing,
36 // Lock blocks until the lock is available.
37 func (rw *RWMutex) Lock() {
38 // Exclusive lock for writers.
39 rw.writerLock.Lock()
40 41 // Flag that we need to be awakened after the last read-lock unlocks.
42 rw.writer.Store(1)
43 44 // Signal to readers that they can't lock this mutex anymore.
45 n := uint32(rwMutexMaxReaders)
46 waiting := rw.readers.Add(-n)
47 if int32(waiting) == -rwMutexMaxReaders {
48 // All readers were already unlocked, so we don't need to wait for them.
49 rw.writer.Store(0)
50 return
51 }
52 53 // There is at least one reader.
54 // Wait until all readers are unlocked. The last reader to unlock will set
55 // rw.writer to 2 and awaken us.
56 for rw.writer.Load() == 1 {
57 rw.writer.Wait(1)
58 }
59 rw.writer.Store(0)
60 }
61 62 // Unlock unlocks rw for writing. It is a run-time error if rw is
63 // not locked for writing on entry to Unlock.
64 //
65 // As with Mutexes, a locked [RWMutex] is not associated with a particular
66 // goroutine. One goroutine may [RWMutex.RLock] ([RWMutex.Lock]) a RWMutex and then
67 // arrange for another goroutine to [RWMutex.RUnlock] ([RWMutex.Unlock]) it.
68 func (rw *RWMutex) Unlock() {
69 // Signal that new readers can lock this mutex.
70 waiting := rw.readers.Add(rwMutexMaxReaders)
71 if waiting != 0 {
72 // Awaken all waiting readers.
73 rw.readers.WakeAll()
74 }
75 76 // Done with this lock (next writer can try to get a lock).
77 rw.writerLock.Unlock()
78 }
79 80 // TryLock tries to lock m and reports whether it succeeded.
81 //
82 // Note that while correct uses of TryLock do exist, they are rare,
83 // and use of TryLock is often a sign of a deeper problem
84 // in a particular use of mutexes.
85 func (rw *RWMutex) TryLock() bool {
86 // Check for active writers
87 if !rw.writerLock.TryLock() {
88 return false
89 }
90 // Have write lock, now check for active readers
91 n := uint32(rwMutexMaxReaders)
92 if !rw.readers.CompareAndSwap(0, -n) {
93 // Active readers, give up write lock
94 rw.writerLock.Unlock()
95 return false
96 }
97 return true
98 }
99 100 // RLock locks rw for reading.
101 //
102 // It should not be used for recursive read locking; a blocked Lock
103 // call excludes new readers from acquiring the lock. See the
104 // documentation on the [RWMutex] type.
105 func (rw *RWMutex) RLock() {
106 // Add us as a reader.
107 newVal := rw.readers.Add(1)
108 109 // Wait until the RWMutex is available for readers.
110 for int32(newVal) <= 0 {
111 rw.readers.Wait(newVal)
112 newVal = rw.readers.Load()
113 }
114 }
115 116 // RUnlock undoes a single [RWMutex.RLock] call;
117 // it does not affect other simultaneous readers.
118 // It is a run-time error if rw is not locked for reading
119 // on entry to RUnlock.
120 func (rw *RWMutex) RUnlock() {
121 // Remove us as a reader.
122 one := uint32(1)
123 readers := int32(rw.readers.Add(-one))
124 125 // Check whether RUnlock was called too often.
126 if readers == -1 || readers == (-rwMutexMaxReaders)-1 {
127 runtimePanic("sync: RUnlock of unlocked RWMutex")
128 }
129 130 if readers == -rwMutexMaxReaders {
131 // This was the last read lock. Check whether we need to wake up a write
132 // lock.
133 if rw.writer.CompareAndSwap(1, 2) {
134 rw.writer.Wake()
135 }
136 }
137 }
138 139 // TryRLock tries to lock rw for reading and reports whether it succeeded.
140 //
141 // Note that while correct uses of TryRLock do exist, they are rare,
142 // and use of TryRLock is often a sign of a deeper problem
143 // in a particular use of mutexes.
144 func (rw *RWMutex) TryRLock() bool {
145 for {
146 c := rw.readers.Load()
147 if c < 0 {
148 // There is a writer waiting or writing.
149 return false
150 }
151 if rw.readers.CompareAndSwap(c, c+1) {
152 // Read lock obtained.
153 return true
154 }
155 }
156 }
157 158 type Locker interface {
159 Lock()
160 Unlock()
161 }
162 163 // RLocker returns a Locker interface that implements
164 // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
165 func (rw *RWMutex) RLocker() Locker {
166 return (*rlocker)(rw)
167 }
168 169 type rlocker RWMutex
170 171 func (r *rlocker) Lock() { (*RWMutex)(r).RLock() }
172 func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }
173