Left: | ||
Right: |
OLD | NEW |
---|---|
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 Loading... | |
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 } |
OLD | NEW |