Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(84)

Side by Side Diff: src/pkg/runtime/linux/thread.c

Issue 4711045: code review 4711045: runtime: improve Linux mutex (Closed)
Patch Set: diff -r 1bd754e69ce7 https://go.googlecode.com/hg/ Created 13 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 #include "runtime.h" 5 #include "runtime.h"
6 #include "defs.h" 6 #include "defs.h"
7 #include "os.h" 7 #include "os.h"
8 #include "stack.h" 8 #include "stack.h"
9 9
10 extern SigTab runtime·sigtab[]; 10 extern SigTab runtime·sigtab[];
11 11
12 // Linux futex. 12 // Linux futex.
13 // 13 //
14 // futexsleep(uint32 *addr, uint32 val) 14 // futexsleep(uint32 *addr, uint32 val)
15 // futexwakeup(uint32 *addr) 15 // futexwakeup(uint32 *addr)
16 // 16 //
17 // Futexsleep atomically checks if *addr == val and if so, sleeps on addr. 17 // Futexsleep atomically checks if *addr == val and if so, sleeps on addr.
18 // Futexwakeup wakes up one thread sleeping on addr. 18 // Futexwakeup wakes up one thread sleeping on addr.
19 // Futexsleep is allowed to wake up spuriously. 19 // Futexsleep is allowed to wake up spuriously.
20 20
21 enum 21 enum
22 { 22 {
23 MUTEX_UNLOCKED = 0,
24 MUTEX_LOCKED = 1,
25 MUTEX_CONTENDED = 3,
26
27 ACTIVE_SPIN = 4,
28 ACTIVE_SPIN_CNT = 30,
29 PASSIVE_SPIN = 1,
30 TOTAL_SPIN = ACTIVE_SPIN + PASSIVE_SPIN,
31
23 FUTEX_WAIT = 0, 32 FUTEX_WAIT = 0,
24 FUTEX_WAKE = 1, 33 FUTEX_WAKE = 1,
25 34
26 EINTR = 4, 35 EINTR = 4,
27 EAGAIN = 11, 36 EAGAIN = 11,
28 }; 37 };
29 38
30 // TODO(rsc): I tried using 1<<40 here but futex woke up (-ETIMEDOUT). 39 // TODO(rsc): I tried using 1<<40 here but futex woke up (-ETIMEDOUT).
31 // I wonder if the timespec that gets to the kernel 40 // I wonder if the timespec that gets to the kernel
32 // actually has two 32-bit numbers in it, so that 41 // actually has two 32-bit numbers in it, so that
33 // a 64-bit 1<<40 ends up being 0 seconds, 42 // a 64-bit 1<<40 ends up being 0 seconds,
34 // 1<<8 nanoseconds. 43 // 1<<8 nanoseconds.
35 static Timespec longtime = 44 static Timespec longtime =
36 { 45 {
37 1<<30, // 34 years 46 1<<30, // 34 years
38 0 47 0
39 }; 48 };
40 49
50 static Timespec shorttime =
51 {
52 0,
53 10000 // 10us
54 };
55
41 // Atomically, 56 // Atomically,
42 // if(*addr == val) sleep 57 // if(*addr == val) sleep
43 // Might be woken up spuriously; that's allowed. 58 // Might be woken up spuriously; that's allowed.
44 static void 59 static void
45 futexsleep(uint32 *addr, uint32 val) 60 futexsleep(uint32 *addr, uint32 val, Timespec *time)
46 { 61 {
47 // Some Linux kernels have a bug where futex of 62 // Some Linux kernels have a bug where futex of
48 // FUTEX_WAIT returns an internal error code 63 // FUTEX_WAIT returns an internal error code
49 // as an errno. Libpthread ignores the return value 64 // as an errno. Libpthread ignores the return value
50 // here, and so can we: as it says a few lines up, 65 // here, and so can we: as it says a few lines up,
51 // spurious wakeups are allowed. 66 // spurious wakeups are allowed.
52 » runtime·futex(addr, FUTEX_WAIT, val, &longtime, nil, 0); 67 » runtime·futex(addr, FUTEX_WAIT, val, time, nil, 0);
53 } 68 }
54 69
55 // If any procs are sleeping on addr, wake up at least one. 70 // If any procs are sleeping on addr, wake up at most one.
iant 2011/07/22 05:38:06 I think the comment is wrong: looks like it now wa
56 static void 71 static void
57 futexwakeup(uint32 *addr) 72 futexwakeup(uint32 *addr, uint32 cnt)
58 { 73 {
59 int64 ret; 74 int64 ret;
60 75
61 » ret = runtime·futex(addr, FUTEX_WAKE, 1, nil, nil, 0); 76 » ret = runtime·futex(addr, FUTEX_WAKE, cnt, nil, nil, 0);
62 77
63 if(ret >= 0) 78 if(ret >= 0)
64 return; 79 return;
65 80
66 // I don't know that futex wakeup can return 81 // I don't know that futex wakeup can return
67 // EAGAIN or EINTR, but if it does, it would be 82 // EAGAIN or EINTR, but if it does, it would be
68 // safe to loop and call futex again. 83 // safe to loop and call futex again.
69 84 » runtime·printf("futexwakeup addr=%p returned %D\n", addr, ret);
70 » runtime·prints("futexwakeup addr=");
71 » runtime·printpointer(addr);
72 » runtime·prints(" returned ");
73 » runtime·printint(ret);
74 » runtime·prints("\n");
75 *(int32*)0x1006 = 0x1006; 85 *(int32*)0x1006 = 0x1006;
76 } 86 }
77 87
78
79 // Lock and unlock.
80 //
81 // The lock state is a single 32-bit word that holds
82 // a 31-bit count of threads waiting for the lock
83 // and a single bit (the low bit) saying whether the lock is held.
84 // The uncontended case runs entirely in user space.
85 // When contention is detected, we defer to the kernel (futex).
86 //
87 // A reminder: compare-and-swap runtime·cas(addr, old, new) does
88 // if(*addr == old) { *addr = new; return 1; }
89 // else return 0;
90 // but atomically.
91
92 static void 88 static void
93 futexlock(Lock *l) 89 futexlock(Lock *l)
94 { 90 {
95 » uint32 v; 91 » uint32 i, v, new, prv;
96 92
97 again: 93 » // Fast path: try to speculatively grab the mutex.
98 » v = l->key; 94 » v = runtime·xchg(&l->key, MUTEX_LOCKED);
99 » if((v&1) == 0){ 95 » if(v == MUTEX_UNLOCKED)
100 » » if(runtime·cas(&l->key, v, v|1)){ 96 » » return;
101 » » » // Lock wasn't held; we grabbed it. 97
102 » » » return; 98 » prv = v;
99 » for(i = 0;; i++) {
100 » » for(;;) {
101 » » » v = l->key;
102 » » » if(v == MUTEX_UNLOCKED) {
103 » » » » new = prv;
104 » » » } else if(v == MUTEX_LOCKED) {
105 » » » » if(i != TOTAL_SPIN)
106 » » » » » break;
107 » » » » new = MUTEX_CONTENDED;
108 » » » } else /*(v == MUTEX_CONTENDED)*/ {
109 » » » » if(i == TOTAL_SPIN)
110 » » » » » break;
111 » » » » new = MUTEX_LOCKED;
112 » » » }
113 » » » if(runtime·cas(&l->key, v, new))
114 » » » » break;
103 } 115 }
104 » » goto again; 116
117 » » if(v == MUTEX_UNLOCKED)
118 » » » break;
119 » » if(v == MUTEX_CONTENDED)
120 » » » prv = MUTEX_CONTENDED;
iant 2011/07/22 05:38:06 I don't understand this. If v == MUTEX_CONTENDED
121
122 » » if(i < ACTIVE_SPIN) {
123 » » » runtime·procyield(ACTIVE_SPIN_CNT);
124 » » } else if(i < TOTAL_SPIN) {
125 » » » runtime·schedyield();
126 » » } else {
127 » » » futexsleep(&l->key, MUTEX_CONTENDED, &shorttime);
128 » » » i = -1;
129 » » }
105 } 130 }
106
107 // Lock was held; try to add ourselves to the waiter count.
108 if(!runtime·cas(&l->key, v, v+2))
109 goto again;
110
111 // We're accounted for, now sleep in the kernel.
112 //
113 // We avoid the obvious lock/unlock race because
114 // the kernel won't put us to sleep if l->key has
115 // changed underfoot and is no longer v+2.
116 //
117 // We only really care that (v&1) == 1 (the lock is held),
118 // and in fact there is a futex variant that could
119 // accommodate that check, but let's not get carried away.)
120 futexsleep(&l->key, v+2);
121
122 // We're awake: remove ourselves from the count.
123 for(;;){
124 v = l->key;
125 if(v < 2)
126 runtime·throw("bad lock key");
127 if(runtime·cas(&l->key, v, v-2))
128 break;
129 }
130
131 // Try for the lock again.
132 goto again;
133 } 131 }
134 132
135 static void 133 static void
136 futexunlock(Lock *l) 134 futexunlock(Lock *l)
137 { 135 {
138 uint32 v; 136 uint32 v;
139 137
140 » // Atomically get value and clear lock bit. 138 » v = runtime·xchg(&l->key, MUTEX_UNLOCKED);
141 again: 139 » if(v == MUTEX_UNLOCKED)
142 » v = l->key;
143 » if((v&1) == 0)
144 runtime·throw("unlock of unlocked lock"); 140 runtime·throw("unlock of unlocked lock");
145 » if(!runtime·cas(&l->key, v, v&~1)) 141 » if(v == MUTEX_CONTENDED)
146 » » goto again; 142 » » futexwakeup(&l->key, 1);
147
148 » // If there were waiters, wake one.
149 » if(v & ~1)
150 » » futexwakeup(&l->key);
151 } 143 }
152 144
153 void 145 void
154 runtime·lock(Lock *l) 146 runtime·lock(Lock *l)
155 { 147 {
156 » if(m->locks < 0) 148 » if(m->locks++ < 0)
157 » » runtime·throw("lock count"); 149 » » runtime·throw("runtime·lock: lock count");
158 » m->locks++;
159 futexlock(l); 150 futexlock(l);
160 } 151 }
161 152
162 void 153 void
163 runtime·unlock(Lock *l) 154 runtime·unlock(Lock *l)
164 { 155 {
165 » m->locks--; 156 » if(--m->locks < 0)
166 » if(m->locks < 0) 157 » » runtime·throw("runtime·unlock: lock count");
167 » » runtime·throw("lock count");
168 futexunlock(l); 158 futexunlock(l);
169 } 159 }
170 160
171 void 161 void
172 runtime·destroylock(Lock*) 162 runtime·destroylock(Lock*)
173 { 163 {
174 } 164 }
175 165
176 166
177 // One-time notifications. 167 // One-time notifications.
178 //
179 // Since the lock/unlock implementation already
180 // takes care of sleeping in the kernel, we just reuse it.
181 // (But it's a weird use, so it gets its own interface.)
182 //
183 // We use a lock to represent the event:
184 // unlocked == event has happened.
185 // Thus the lock starts out locked, and to wait for the
186 // event you try to lock the lock. To signal the event,
187 // you unlock the lock.
188
189 void 168 void
190 runtime·noteclear(Note *n) 169 runtime·noteclear(Note *n)
191 { 170 {
192 » n->lock.key = 0;» // memset(n, 0, sizeof *n) 171 » n->state = 0;
193 » futexlock(&n->lock);
194 } 172 }
195 173
196 void 174 void
197 runtime·notewakeup(Note *n) 175 runtime·notewakeup(Note *n)
198 { 176 {
199 » futexunlock(&n->lock); 177 » runtime·xchg(&n->state, 1);
178 » futexwakeup(&n->state, 1<<30);
200 } 179 }
201 180
202 void 181 void
203 runtime·notesleep(Note *n) 182 runtime·notesleep(Note *n)
204 { 183 {
205 » futexlock(&n->lock); 184 » while(runtime·atomicload(&n->state) == 0)
206 » futexunlock(&n->lock);» // Let other sleepers find out too. 185 » » futexsleep(&n->state, 0, &longtime);
207 } 186 }
208 187
209 188
210 // Clone, the Linux rfork. 189 // Clone, the Linux rfork.
211 enum 190 enum
212 { 191 {
213 CLONE_VM = 0x100, 192 CLONE_VM = 0x100,
214 CLONE_FS = 0x200, 193 CLONE_FS = 0x200,
215 CLONE_FILES = 0x400, 194 CLONE_FILES = 0x400,
216 CLONE_SIGHAND = 0x800, 195 CLONE_SIGHAND = 0x800,
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
296 switch(g->sigcode0) { 275 switch(g->sigcode0) {
297 case FPE_INTDIV: 276 case FPE_INTDIV:
298 runtime·panicstring("integer divide by zero"); 277 runtime·panicstring("integer divide by zero");
299 case FPE_INTOVF: 278 case FPE_INTOVF:
300 runtime·panicstring("integer overflow"); 279 runtime·panicstring("integer overflow");
301 } 280 }
302 runtime·panicstring("floating point error"); 281 runtime·panicstring("floating point error");
303 } 282 }
304 runtime·panicstring(runtime·sigtab[g->sig].name); 283 runtime·panicstring(runtime·sigtab[g->sig].name);
305 } 284 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b