4
4
5
5
package sync
6
6
7
- import "sync/atomic"
7
+ import (
8
+ "runtime"
9
+ "sync/atomic"
10
+ )
8
11
9
12
// An RWMutex is a reader/writer mutual exclusion lock.
10
13
// The lock can be held by an arbitrary number of readers
11
14
// or a single writer.
12
15
// RWMutexes can be created as part of other
13
16
// structures; the zero value for a RWMutex is
14
17
// an unlocked mutex.
15
- //
16
- // Writers take priority over Readers: no new RLocks
17
- // are granted while a blocked Lock call is waiting.
18
18
type RWMutex struct {
19
- w Mutex // held if there are pending readers or writers
20
- r Mutex // held if the w is being rd
21
- readerCount int32 // number of pending readers
19
+ w Mutex // held if there are pending writers
20
+ writerSem uint32 // semaphore for writers to wait for completing readers
21
+ readerSem uint32 // semaphore for readers to wait for completing writers
22
+ readerCount int32 // number of pending readers
23
+ readerWait int32 // number of departing readers
22
24
}
23
25
26
+ const rwmutexMaxReaders = 1 << 30
27
+
24
28
// RLock locks rw for reading.
25
- // If the lock is already locked for writing or there is a writer already waiting
26
- // to release the lock, RLock blocks until the writer has released the lock.
27
29
func (rw * RWMutex ) RLock () {
28
- // Use rw.r.Lock() to block granting the RLock if a goroutine
29
- // is waiting for its Lock. This is the prevent starvation of W in
30
- // this situation:
31
- // A: rw.RLock() // granted
32
- // W: rw.Lock() // waiting for rw.w().Lock()
33
- // B: rw.RLock() // granted
34
- // C: rw.RLock() // granted
35
- // B: rw.RUnlock()
36
- // ... (new readers come and go indefinitely, W is starving)
37
- rw .r .Lock ()
38
- if atomic .AddInt32 (& rw .readerCount , 1 ) == 1 {
39
- // The first reader locks rw.w, so writers will be blocked
40
- // while the readers have the RLock.
41
- rw .w .Lock ()
30
+ if atomic .AddInt32 (& rw .readerCount , 1 ) < 0 {
31
+ // A writer is pending, wait for it.
32
+ runtime .Semacquire (& rw .readerSem )
42
33
}
43
- rw .r .Unlock ()
44
34
}
45
35
46
36
// RUnlock undoes a single RLock call;
47
37
// it does not affect other simultaneous readers.
48
38
// It is a run-time error if rw is not locked for reading
49
39
// on entry to RUnlock.
50
40
func (rw * RWMutex ) RUnlock () {
51
- if atomic .AddInt32 (& rw .readerCount , - 1 ) == 0 {
52
- // last reader finished, enable writers
53
- rw .w .Unlock ()
41
+ if atomic .AddInt32 (& rw .readerCount , - 1 ) < 0 {
42
+ // A writer is pending.
43
+ if atomic .AddInt32 (& rw .readerWait , - 1 ) == 0 {
44
+ // The last reader unblocks the writer.
45
+ runtime .Semrelease (& rw .writerSem )
46
+ }
54
47
}
55
48
}
56
49
@@ -61,9 +54,14 @@ func (rw *RWMutex) RUnlock() {
61
54
// a blocked Lock call excludes new readers from acquiring
62
55
// the lock.
63
56
func (rw * RWMutex ) Lock () {
64
- rw . r . Lock ()
57
+ // First, resolve competition with other writers.
65
58
rw .w .Lock ()
66
- rw .r .Unlock ()
59
+ // Announce to readers there is a pending writer.
60
+ r := atomic .AddInt32 (& rw .readerCount , - rwmutexMaxReaders ) + rwmutexMaxReaders
61
+ // Wait for active readers.
62
+ if r != 0 && atomic .AddInt32 (& rw .readerWait , r ) != 0 {
63
+ runtime .Semacquire (& rw .writerSem )
64
+ }
67
65
}
68
66
69
67
// Unlock unlocks rw for writing. It is a run-time error if rw is
@@ -72,7 +70,16 @@ func (rw *RWMutex) Lock() {
72
70
// As with Mutexes, a locked RWMutex is not associated with a particular
73
71
// goroutine. One goroutine may RLock (Lock) an RWMutex and then
74
72
// arrange for another goroutine to RUnlock (Unlock) it.
75
- func (rw * RWMutex ) Unlock () { rw .w .Unlock () }
73
+ func (rw * RWMutex ) Unlock () {
74
+ // Announce to readers there is no active writer.
75
+ r := atomic .AddInt32 (& rw .readerCount , rwmutexMaxReaders )
76
+ // Unblock blocked readers, if any.
77
+ for i := 0 ; i < int (r ); i ++ {
78
+ runtime .Semrelease (& rw .readerSem )
79
+ }
80
+ // Allow other writers to proceed.
81
+ rw .w .Unlock ()
82
+ }
76
83
77
84
// RLocker returns a Locker interface that implements
78
85
// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
0 commit comments