github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/runtime/lock_sema.c (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build darwin netbsd openbsd plan9 windows 6 7 #include "runtime.h" 8 #include "stack.h" 9 #include "../../cmd/ld/textflag.h" 10 11 // This implementation depends on OS-specific implementations of 12 // 13 // uintptr runtime·semacreate(void) 14 // Create a semaphore, which will be assigned to m->waitsema. 15 // The zero value is treated as absence of any semaphore, 16 // so be sure to return a non-zero value. 17 // 18 // int32 runtime·semasleep(int64 ns) 19 // If ns < 0, acquire m->waitsema and return 0. 20 // If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds. 21 // Return 0 if the semaphore was acquired, -1 if interrupted or timed out. 22 // 23 // int32 runtime·semawakeup(M *mp) 24 // Wake up mp, which is or will soon be sleeping on mp->waitsema. 25 // 26 27 enum 28 { 29 LOCKED = 1, 30 31 ACTIVE_SPIN = 4, 32 ACTIVE_SPIN_CNT = 30, 33 PASSIVE_SPIN = 1, 34 }; 35 36 void 37 runtime·lock(Lock *l) 38 { 39 uintptr v; 40 uint32 i, spin; 41 42 if(m->locks++ < 0) 43 runtime·throw("runtime·lock: lock count"); 44 45 // Speculative grab for lock. 46 if(runtime·casp((void**)&l->key, nil, (void*)LOCKED)) 47 return; 48 49 if(m->waitsema == 0) 50 m->waitsema = runtime·semacreate(); 51 52 // On uniprocessor's, no point spinning. 53 // On multiprocessors, spin for ACTIVE_SPIN attempts. 54 spin = 0; 55 if(runtime·ncpu > 1) 56 spin = ACTIVE_SPIN; 57 58 for(i=0;; i++) { 59 v = (uintptr)runtime·atomicloadp((void**)&l->key); 60 if((v&LOCKED) == 0) { 61 unlocked: 62 if(runtime·casp((void**)&l->key, (void*)v, (void*)(v|LOCKED))) 63 return; 64 i = 0; 65 } 66 if(i<spin) 67 runtime·procyield(ACTIVE_SPIN_CNT); 68 else if(i<spin+PASSIVE_SPIN) 69 runtime·osyield(); 70 else { 71 // Someone else has it. 72 // l->waitm points to a linked list of M's waiting 73 // for this lock, chained through m->nextwaitm. 74 // Queue this M. 75 for(;;) { 76 m->nextwaitm = (void*)(v&~LOCKED); 77 if(runtime·casp((void**)&l->key, (void*)v, (void*)((uintptr)m|LOCKED))) 78 break; 79 v = (uintptr)runtime·atomicloadp((void**)&l->key); 80 if((v&LOCKED) == 0) 81 goto unlocked; 82 } 83 if(v&LOCKED) { 84 // Queued. Wait. 85 runtime·semasleep(-1); 86 i = 0; 87 } 88 } 89 } 90 } 91 92 void 93 runtime·unlock(Lock *l) 94 { 95 uintptr v; 96 M *mp; 97 98 for(;;) { 99 v = (uintptr)runtime·atomicloadp((void**)&l->key); 100 if(v == LOCKED) { 101 if(runtime·casp((void**)&l->key, (void*)LOCKED, nil)) 102 break; 103 } else { 104 // Other M's are waiting for the lock. 105 // Dequeue an M. 106 mp = (void*)(v&~LOCKED); 107 if(runtime·casp((void**)&l->key, (void*)v, mp->nextwaitm)) { 108 // Dequeued an M. Wake it. 109 runtime·semawakeup(mp); 110 break; 111 } 112 } 113 } 114 115 if(--m->locks < 0) 116 runtime·throw("runtime·unlock: lock count"); 117 if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack 118 g->stackguard0 = StackPreempt; 119 } 120 121 // One-time notifications. 122 void 123 runtime·noteclear(Note *n) 124 { 125 n->key = 0; 126 } 127 128 void 129 runtime·notewakeup(Note *n) 130 { 131 M *mp; 132 133 do 134 mp = runtime·atomicloadp((void**)&n->key); 135 while(!runtime·casp((void**)&n->key, mp, (void*)LOCKED)); 136 137 // Successfully set waitm to LOCKED. 138 // What was it before? 139 if(mp == nil) { 140 // Nothing was waiting. Done. 141 } else if(mp == (M*)LOCKED) { 142 // Two notewakeups! Not allowed. 143 runtime·throw("notewakeup - double wakeup"); 144 } else { 145 // Must be the waiting m. Wake it up. 146 runtime·semawakeup(mp); 147 } 148 } 149 150 void 151 runtime·notesleep(Note *n) 152 { 153 if(g != m->g0) 154 runtime·throw("notesleep not on g0"); 155 156 if(m->waitsema == 0) 157 m->waitsema = runtime·semacreate(); 158 if(!runtime·casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup) 159 if(n->key != LOCKED) 160 runtime·throw("notesleep - waitm out of sync"); 161 return; 162 } 163 // Queued. Sleep. 164 runtime·semasleep(-1); 165 } 166 167 #pragma textflag NOSPLIT 168 static bool 169 notetsleep(Note *n, int64 ns, int64 deadline, M *mp) 170 { 171 // Conceptually, deadline and mp are local variables. 172 // They are passed as arguments so that the space for them 173 // does not count against our nosplit stack sequence. 174 175 // Register for wakeup on n->waitm. 176 if(!runtime·casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup already) 177 if(n->key != LOCKED) 178 runtime·throw("notetsleep - waitm out of sync"); 179 return true; 180 } 181 182 if(ns < 0) { 183 // Queued. Sleep. 184 runtime·semasleep(-1); 185 return true; 186 } 187 188 deadline = runtime·nanotime() + ns; 189 for(;;) { 190 // Registered. Sleep. 191 if(runtime·semasleep(ns) >= 0) { 192 // Acquired semaphore, semawakeup unregistered us. 193 // Done. 194 return true; 195 } 196 197 // Interrupted or timed out. Still registered. Semaphore not acquired. 198 ns = deadline - runtime·nanotime(); 199 if(ns <= 0) 200 break; 201 // Deadline hasn't arrived. Keep sleeping. 202 } 203 204 // Deadline arrived. Still registered. Semaphore not acquired. 205 // Want to give up and return, but have to unregister first, 206 // so that any notewakeup racing with the return does not 207 // try to grant us the semaphore when we don't expect it. 208 for(;;) { 209 mp = runtime·atomicloadp((void**)&n->key); 210 if(mp == m) { 211 // No wakeup yet; unregister if possible. 212 if(runtime·casp((void**)&n->key, mp, nil)) 213 return false; 214 } else if(mp == (M*)LOCKED) { 215 // Wakeup happened so semaphore is available. 216 // Grab it to avoid getting out of sync. 217 if(runtime·semasleep(-1) < 0) 218 runtime·throw("runtime: unable to acquire - semaphore out of sync"); 219 return true; 220 } else 221 runtime·throw("runtime: unexpected waitm - semaphore out of sync"); 222 } 223 } 224 225 bool 226 runtime·notetsleep(Note *n, int64 ns) 227 { 228 bool res; 229 230 if(g != m->g0 && !m->gcing) 231 runtime·throw("notetsleep not on g0"); 232 233 if(m->waitsema == 0) 234 m->waitsema = runtime·semacreate(); 235 236 res = notetsleep(n, ns, 0, nil); 237 return res; 238 } 239 240 // same as runtime·notetsleep, but called on user g (not g0) 241 // calls only nosplit functions between entersyscallblock/exitsyscall 242 bool 243 runtime·notetsleepg(Note *n, int64 ns) 244 { 245 bool res; 246 247 if(g == m->g0) 248 runtime·throw("notetsleepg on g0"); 249 250 if(m->waitsema == 0) 251 m->waitsema = runtime·semacreate(); 252 253 runtime·entersyscallblock(); 254 res = notetsleep(n, ns, 0, nil); 255 runtime·exitsyscall(); 256 return res; 257 }