github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/runtime/lock_futex.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 dragonfly freebsd linux 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 // runtime·futexsleep(uint32 *addr, uint32 val, int64 ns) 14 // Atomically, 15 // if(*addr == val) sleep 16 // Might be woken up spuriously; that's allowed. 17 // Don't sleep longer than ns; ns < 0 means forever. 18 // 19 // runtime·futexwakeup(uint32 *addr, uint32 cnt) 20 // If any procs are sleeping on addr, wake up at most cnt. 21 22 enum 23 { 24 MUTEX_UNLOCKED = 0, 25 MUTEX_LOCKED = 1, 26 MUTEX_SLEEPING = 2, 27 28 ACTIVE_SPIN = 4, 29 ACTIVE_SPIN_CNT = 30, 30 PASSIVE_SPIN = 1, 31 }; 32 33 // Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING. 34 // MUTEX_SLEEPING means that there is presumably at least one sleeping thread. 35 // Note that there can be spinning threads during all states - they do not 36 // affect mutex's state. 37 void 38 runtime·lock(Lock *l) 39 { 40 uint32 i, v, wait, spin; 41 42 if(m->locks++ < 0) 43 runtime·throw("runtime·lock: lock count"); 44 45 // Speculative grab for lock. 46 v = runtime·xchg((uint32*)&l->key, MUTEX_LOCKED); 47 if(v == MUTEX_UNLOCKED) 48 return; 49 50 // wait is either MUTEX_LOCKED or MUTEX_SLEEPING 51 // depending on whether there is a thread sleeping 52 // on this mutex. If we ever change l->key from 53 // MUTEX_SLEEPING to some other value, we must be 54 // careful to change it back to MUTEX_SLEEPING before 55 // returning, to ensure that the sleeping thread gets 56 // its wakeup call. 57 wait = v; 58 59 // On uniprocessor's, no point spinning. 60 // On multiprocessors, spin for ACTIVE_SPIN attempts. 61 spin = 0; 62 if(runtime·ncpu > 1) 63 spin = ACTIVE_SPIN; 64 65 for(;;) { 66 // Try for lock, spinning. 67 for(i = 0; i < spin; i++) { 68 while(l->key == MUTEX_UNLOCKED) 69 if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait)) 70 return; 71 runtime·procyield(ACTIVE_SPIN_CNT); 72 } 73 74 // Try for lock, rescheduling. 75 for(i=0; i < PASSIVE_SPIN; i++) { 76 while(l->key == MUTEX_UNLOCKED) 77 if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait)) 78 return; 79 runtime·osyield(); 80 } 81 82 // Sleep. 83 v = runtime·xchg((uint32*)&l->key, MUTEX_SLEEPING); 84 if(v == MUTEX_UNLOCKED) 85 return; 86 wait = MUTEX_SLEEPING; 87 runtime·futexsleep((uint32*)&l->key, MUTEX_SLEEPING, -1); 88 } 89 } 90 91 void 92 runtime·unlock(Lock *l) 93 { 94 uint32 v; 95 96 v = runtime·xchg((uint32*)&l->key, MUTEX_UNLOCKED); 97 if(v == MUTEX_UNLOCKED) 98 runtime·throw("unlock of unlocked lock"); 99 if(v == MUTEX_SLEEPING) 100 runtime·futexwakeup((uint32*)&l->key, 1); 101 102 if(--m->locks < 0) 103 runtime·throw("runtime·unlock: lock count"); 104 if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack 105 g->stackguard0 = StackPreempt; 106 } 107 108 // One-time notifications. 109 void 110 runtime·noteclear(Note *n) 111 { 112 n->key = 0; 113 } 114 115 void 116 runtime·notewakeup(Note *n) 117 { 118 uint32 old; 119 120 old = runtime·xchg((uint32*)&n->key, 1); 121 if(old != 0) { 122 runtime·printf("notewakeup - double wakeup (%d)\n", old); 123 runtime·throw("notewakeup - double wakeup"); 124 } 125 runtime·futexwakeup((uint32*)&n->key, 1); 126 } 127 128 void 129 runtime·notesleep(Note *n) 130 { 131 if(g != m->g0) 132 runtime·throw("notesleep not on g0"); 133 while(runtime·atomicload((uint32*)&n->key) == 0) 134 runtime·futexsleep((uint32*)&n->key, 0, -1); 135 } 136 137 #pragma textflag NOSPLIT 138 static bool 139 notetsleep(Note *n, int64 ns, int64 deadline, int64 now) 140 { 141 // Conceptually, deadline and now are local variables. 142 // They are passed as arguments so that the space for them 143 // does not count against our nosplit stack sequence. 144 145 if(ns < 0) { 146 while(runtime·atomicload((uint32*)&n->key) == 0) 147 runtime·futexsleep((uint32*)&n->key, 0, -1); 148 return true; 149 } 150 151 if(runtime·atomicload((uint32*)&n->key) != 0) 152 return true; 153 154 deadline = runtime·nanotime() + ns; 155 for(;;) { 156 runtime·futexsleep((uint32*)&n->key, 0, ns); 157 if(runtime·atomicload((uint32*)&n->key) != 0) 158 break; 159 now = runtime·nanotime(); 160 if(now >= deadline) 161 break; 162 ns = deadline - now; 163 } 164 return runtime·atomicload((uint32*)&n->key) != 0; 165 } 166 167 bool 168 runtime·notetsleep(Note *n, int64 ns) 169 { 170 bool res; 171 172 if(g != m->g0 && !m->gcing) 173 runtime·throw("notetsleep not on g0"); 174 175 res = notetsleep(n, ns, 0, 0); 176 return res; 177 } 178 179 // same as runtime·notetsleep, but called on user g (not g0) 180 // calls only nosplit functions between entersyscallblock/exitsyscall 181 bool 182 runtime·notetsleepg(Note *n, int64 ns) 183 { 184 bool res; 185 186 if(g == m->g0) 187 runtime·throw("notetsleepg on g0"); 188 189 runtime·entersyscallblock(); 190 res = notetsleep(n, ns, 0, 0); 191 runtime·exitsyscall(); 192 return res; 193 }