@@ -23,19 +23,20 @@ import (
23
23
// If any procs are sleeping on addr, wake up at most cnt.
24
24
25
25
const (
26
- mutex_unlocked = 0
27
- mutex_locked = 1
28
- mutex_sleeping = 2
26
+ mutex_locked = 0x1
27
+ mutex_sleeping = 0x2 // Ensure futex's low 32 bits won't be all zeros
29
28
30
29
active_spin = 4
31
30
active_spin_cnt = 30
32
31
passive_spin = 1
33
32
)
34
33
35
- // Possible lock states are mutex_unlocked, mutex_locked and mutex_sleeping.
36
- // mutex_sleeping means that there is presumably at least one sleeping thread.
37
- // Note that there can be spinning threads during all states - they do not
38
- // affect mutex's state.
34
+ // The mutex.key holds two state flags in its lowest bits: When the mutex_locked
35
+ // bit is set, the mutex is locked. When the mutex_sleeping bit is set, a thread
36
+ // is waiting in futexsleep for the mutex to be available. These flags operate
37
+ // independently: a thread can enter lock2, observe that another thread is
38
+ // already asleep, and immediately try to grab the lock anyway without waiting
39
+ // for its "fair" turn.
39
40
40
41
// We use the uintptr mutex.key and note.key as a uint32.
41
42
//
@@ -54,27 +55,16 @@ func lock(l *mutex) {
54
55
55
56
func lock2 (l * mutex ) {
56
57
gp := getg ()
57
-
58
58
if gp .m .locks < 0 {
59
59
throw ("runtime·lock: lock count" )
60
60
}
61
61
gp .m .locks ++
62
62
63
63
// Speculative grab for lock.
64
- v := atomic .Xchg (key32 (& l .key ), mutex_locked )
65
- if v == mutex_unlocked {
64
+ if atomic .Casuintptr (& l .key , 0 , mutex_locked ) {
66
65
return
67
66
}
68
67
69
- // wait is either MUTEX_LOCKED or MUTEX_SLEEPING
70
- // depending on whether there is a thread sleeping
71
- // on this mutex. If we ever change l->key from
72
- // MUTEX_SLEEPING to some other value, we must be
73
- // careful to change it back to MUTEX_SLEEPING before
74
- // returning, to ensure that the sleeping thread gets
75
- // its wakeup call.
76
- wait := v
77
-
78
68
timer := & lockTimer {lock : l }
79
69
timer .begin ()
80
70
// On uniprocessors, no point spinning.
@@ -83,37 +73,39 @@ func lock2(l *mutex) {
83
73
if ncpu > 1 {
84
74
spin = active_spin
85
75
}
86
- for {
87
- // Try for lock, spinning.
88
- for i := 0 ; i < spin ; i ++ {
89
- for l . key == mutex_unlocked {
90
- if atomic . Cas ( key32 ( & l . key ), mutex_unlocked , wait ) {
91
- timer . end ()
92
- return
93
- }
76
+ Loop:
77
+ for i := 0 ; ; i ++ {
78
+ v := atomic . Loaduintptr ( & l . key )
79
+ if v & mutex_locked == 0 {
80
+ // Unlocked. Try to lock.
81
+ if atomic . Casuintptr ( & l . key , v , v | mutex_locked ) {
82
+ timer . end ()
83
+ return
94
84
}
95
- procyield ( active_spin_cnt )
85
+ i = 0
96
86
}
97
-
98
- // Try for lock, rescheduling.
99
- for i := 0 ; i < passive_spin ; i ++ {
100
- for l .key == mutex_unlocked {
101
- if atomic .Cas (key32 (& l .key ), mutex_unlocked , wait ) {
102
- timer .end ()
103
- return
87
+ if i < spin {
88
+ procyield (active_spin_cnt )
89
+ } else if i < spin + passive_spin {
90
+ osyield ()
91
+ } else {
92
+ // Someone else has it.
93
+ for {
94
+ head := v &^ (mutex_locked | mutex_sleeping )
95
+ if atomic .Casuintptr (& l .key , v , head | mutex_locked | mutex_sleeping ) {
96
+ break
97
+ }
98
+ v = atomic .Loaduintptr (& l .key )
99
+ if v & mutex_locked == 0 {
100
+ continue Loop
104
101
}
105
102
}
106
- osyield ()
107
- }
108
-
109
- // Sleep.
110
- v = atomic .Xchg (key32 (& l .key ), mutex_sleeping )
111
- if v == mutex_unlocked {
112
- timer .end ()
113
- return
103
+ if v & mutex_locked != 0 {
104
+ // Queued. Wait.
105
+ futexsleep (key32 (& l .key ), uint32 (v ), - 1 )
106
+ i = 0
107
+ }
114
108
}
115
- wait = mutex_sleeping
116
- futexsleep (key32 (& l .key ), mutex_sleeping , - 1 )
117
109
}
118
110
}
119
111
@@ -122,12 +114,21 @@ func unlock(l *mutex) {
122
114
}
123
115
124
116
func unlock2 (l * mutex ) {
125
- v := atomic .Xchg (key32 (& l .key ), mutex_unlocked )
126
- if v == mutex_unlocked {
127
- throw ("unlock of unlocked lock" )
128
- }
129
- if v == mutex_sleeping {
130
- futexwakeup (key32 (& l .key ), 1 )
117
+ for {
118
+ v := atomic .Loaduintptr (& l .key )
119
+ if v == mutex_locked {
120
+ if atomic .Casuintptr (& l .key , mutex_locked , 0 ) {
121
+ break
122
+ }
123
+ } else if v & mutex_locked == 0 {
124
+ throw ("unlock of unlocked lock" )
125
+ } else {
126
+ // Other M's are waiting for the lock.
127
+ if atomic .Casuintptr (& l .key , v , v &^mutex_locked ) {
128
+ futexwakeup (key32 (& l .key ), 1 )
129
+ break
130
+ }
131
+ }
131
132
}
132
133
133
134
gp := getg ()
0 commit comments