@@ -12,6 +12,9 @@ type Mutex struct {
1212//go:linkname scheduleTask runtime.scheduleTask
1313func scheduleTask (* task.Task )
1414
15+ //go:linkname runtimePanic runtime.runtimePanic
16+ func runtimePanic (msg string )
17+
1518func (m * Mutex ) Lock () {
1619 // Fast path: try to take an uncontended lock.
1720 if m .futex .CompareAndSwap (0 , 1 ) {
@@ -52,130 +55,92 @@ func (m *Mutex) TryLock() bool {
5255}
5356
5457type RWMutex struct {
55- // waitingWriters are all of the tasks waiting for write locks.
56- waitingWriters task.Stack
57-
58- // waitingReaders are all of the tasks waiting for a read lock.
59- waitingReaders task.Stack
60-
61- // state is the current state of the RWMutex.
62- // Iff the mutex is completely unlocked, it contains rwMutexStateUnlocked (aka 0).
63- // Iff the mutex is write-locked, it contains rwMutexStateWLocked.
64- // While the mutex is read-locked, it contains the current number of readers.
65- state uint32
58+ // Reader count, with the number of readers that currently have read-locked
59+ // this mutex.
60+ // The value can be in two states: one where 0 means no readers and another
61+ // where -rwMutexMaxReaders means no readers. A base of 0 is normal
62+ // uncontended operation, a base of -rwMutexMaxReaders means a writer has
63+ // the lock or is trying to get the lock. In the second case, readers should
64+ // wait until the reader count becomes non-negative again to give the writer
65+ // a chance to obtain the lock.
66+ readers task.Futex
67+
68+ // Writer futex, normally 0. If there is a writer waiting until all readers
69+ // have unlocked, this value is 1. It will be changed to a 2 (and get a
70+ // wake) when the last reader unlocks.
71+ writer task.Futex
72+
73+ // Writer lock. Held between Lock() and Unlock().
74+ writerLock Mutex
6675}
6776
68- const (
69- rwMutexStateUnlocked = uint32 (0 )
70- rwMutexStateWLocked = ^ uint32 (0 )
71- rwMutexMaxReaders = rwMutexStateWLocked - 1
72- )
77+ const rwMutexMaxReaders = 1 << 30
7378
7479func (rw * RWMutex ) Lock () {
75- if rw .state == 0 {
76- // The mutex is completely unlocked.
77- // Lock without waiting.
78- rw .state = rwMutexStateWLocked
80+ // Exclusive lock for writers.
81+ rw .writerLock .Lock ()
82+
83+ // Flag that we need to be awakened after the last read-lock unlocks.
84+ rw .writer .Store (1 )
85+
86+ // Signal to readers that they can't lock this mutex anymore.
87+ n := uint32 (rwMutexMaxReaders )
88+ waiting := rw .readers .Add (- n )
89+ if int32 (waiting ) == - rwMutexMaxReaders {
90+ // All readers were already unlocked, so we don't need to wait for them.
91+ rw .writer .Store (0 )
7992 return
8093 }
8194
82- // Wait for the lock to be released.
83- rw .waitingWriters .Push (task .Current ())
84- task .Pause ()
95+ // There is at least one reader.
96+ // Wait until all readers are unlocked. The last reader to unlock will set
97+ // rw.writer to 2 and awaken us.
98+ for rw .writer .Load () == 1 {
99+ rw .writer .Wait (1 )
100+ }
101+ rw .writer .Store (0 )
85102}
86103
87104func (rw * RWMutex ) Unlock () {
88- switch rw .state {
89- case rwMutexStateWLocked :
90- // This is correct.
91-
92- case rwMutexStateUnlocked :
93- // The mutex is already unlocked.
94- panic ("sync: unlock of unlocked RWMutex" )
95-
96- default :
97- // The mutex is read-locked instead of write-locked.
98- panic ("sync: write-unlock of read-locked RWMutex" )
105+ // Signal that new readers can lock this mutex.
106+ waiting := rw .readers .Add (rwMutexMaxReaders )
107+ if waiting != 0 {
108+ // Awaken all waiting readers.
109+ rw .readers .WakeAll ()
99110 }
100111
101- switch {
102- case rw .maybeUnblockReaders ():
103- // Switched over to read mode.
104-
105- case rw .maybeUnblockWriter ():
106- // Transferred to another writer.
107-
108- default :
109- // Nothing is waiting for the lock.
110- rw .state = rwMutexStateUnlocked
111- }
112+ // Done with this lock (next writer can try to get a lock).
113+ rw .writerLock .Unlock ()
112114}
113115
114116func (rw * RWMutex ) RLock () {
115- if rw .state == rwMutexStateWLocked {
116- // Wait for the write lock to be released.
117- rw .waitingReaders .Push (task .Current ())
118- task .Pause ()
119- return
120- }
117+ // Add us as a reader.
118+ newVal := rw .readers .Add (1 )
121119
122- if rw .state == rwMutexMaxReaders {
123- panic ("sync: too many readers on RWMutex" )
120+ // Wait until the RWMutex is available for readers.
121+ for int32 (newVal ) <= 0 {
122+ rw .readers .Wait (newVal )
123+ newVal = rw .readers .Load ()
124124 }
125-
126- // Increase the reader count.
127- rw .state ++
128125}
129126
130127func (rw * RWMutex ) RUnlock () {
131- switch rw .state {
132- case rwMutexStateUnlocked :
133- // The mutex is already unlocked.
134- panic ("sync: unlock of unlocked RWMutex" )
135-
136- case rwMutexStateWLocked :
137- // The mutex is write-locked instead of read-locked.
138- panic ("sync: read-unlock of write-locked RWMutex" )
139- }
140-
141- rw .state --
128+ // Remove us as a reader.
129+ one := uint32 (1 )
130+ readers := int32 (rw .readers .Add (- one ))
142131
143- if rw .state == rwMutexStateUnlocked {
144- // This was the last reader.
145- // Try to unblock a writer.
146- rw .maybeUnblockWriter ()
132+ // Check whether RUnlock was called too often.
133+ if readers == - 1 || readers == (- rwMutexMaxReaders )- 1 {
134+ runtimePanic ("sync: RUnlock of unlocked RWMutex" )
147135 }
148- }
149136
150- func (rw * RWMutex ) maybeUnblockReaders () bool {
151- var n uint32
152- for {
153- t := rw .waitingReaders .Pop ()
154- if t == nil {
155- break
137+ if readers == - rwMutexMaxReaders {
138+ // This was the last read lock. Check whether we need to wake up a write
139+ // lock.
140+ if rw .writer .CompareAndSwap (1 , 2 ) {
141+ rw .writer .Wake ()
156142 }
157-
158- n ++
159- scheduleTask (t )
160- }
161- if n == 0 {
162- return false
163143 }
164-
165- rw .state = n
166- return true
167- }
168-
169- func (rw * RWMutex ) maybeUnblockWriter () bool {
170- t := rw .waitingWriters .Pop ()
171- if t == nil {
172- return false
173- }
174-
175- rw .state = rwMutexStateWLocked
176- scheduleTask (t )
177-
178- return true
179144}
180145
181146type Locker interface {
0 commit comments