github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/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 freebsd linux 6 7 #include "runtime.h" 8 9 // This implementation depends on OS-specific implementations of 10 // 11 // runtime·futexsleep(uint32 *addr, uint32 val, int64 ns) 12 // Atomically, 13 // if(*addr == val) sleep 14 // Might be woken up spuriously; that's allowed. 15 // Don't sleep longer than ns; ns < 0 means forever. 16 // 17 // runtime·futexwakeup(uint32 *addr, uint32 cnt) 18 // If any procs are sleeping on addr, wake up at most cnt. 19 20 enum 21 { 22 MUTEX_UNLOCKED = 0, 23 MUTEX_LOCKED = 1, 24 MUTEX_SLEEPING = 2, 25 26 ACTIVE_SPIN = 4, 27 ACTIVE_SPIN_CNT = 30, 28 PASSIVE_SPIN = 1, 29 }; 30 31 // Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING. 32 // MUTEX_SLEEPING means that there is presumably at least one sleeping thread. 33 // Note that there can be spinning threads during all states - they do not 34 // affect mutex's state. 35 void 36 runtime·lock(Lock *l) 37 { 38 uint32 i, v, wait, spin; 39 40 if(m->locks++ < 0) 41 runtime·throw("runtime·lock: lock count"); 42 43 // Speculative grab for lock. 44 v = runtime·xchg((uint32*)&l->key, MUTEX_LOCKED); 45 if(v == MUTEX_UNLOCKED) 46 return; 47 48 // wait is either MUTEX_LOCKED or MUTEX_SLEEPING 49 // depending on whether there is a thread sleeping 50 // on this mutex. If we ever change l->key from 51 // MUTEX_SLEEPING to some other value, we must be 52 // careful to change it back to MUTEX_SLEEPING before 53 // returning, to ensure that the sleeping thread gets 54 // its wakeup call. 55 wait = v; 56 57 // On uniprocessor's, no point spinning. 58 // On multiprocessors, spin for ACTIVE_SPIN attempts. 59 spin = 0; 60 if(runtime·ncpu > 1) 61 spin = ACTIVE_SPIN; 62 63 for(;;) { 64 // Try for lock, spinning. 65 for(i = 0; i < spin; i++) { 66 while(l->key == MUTEX_UNLOCKED) 67 if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait)) 68 return; 69 runtime·procyield(ACTIVE_SPIN_CNT); 70 } 71 72 // Try for lock, rescheduling. 73 for(i=0; i < PASSIVE_SPIN; i++) { 74 while(l->key == MUTEX_UNLOCKED) 75 if(runtime·cas((uint32*)&l->key, MUTEX_UNLOCKED, wait)) 76 return; 77 runtime·osyield(); 78 } 79 80 // Sleep. 81 v = runtime·xchg((uint32*)&l->key, MUTEX_SLEEPING); 82 if(v == MUTEX_UNLOCKED) 83 return; 84 wait = MUTEX_SLEEPING; 85 runtime·futexsleep((uint32*)&l->key, MUTEX_SLEEPING, -1); 86 } 87 } 88 89 void 90 runtime·unlock(Lock *l) 91 { 92 uint32 v; 93 94 if(--m->locks < 0) 95 runtime·throw("runtime·unlock: lock count"); 96 97 v = runtime·xchg((uint32*)&l->key, MUTEX_UNLOCKED); 98 if(v == MUTEX_UNLOCKED) 99 runtime·throw("unlock of unlocked lock"); 100 if(v == MUTEX_SLEEPING) 101 runtime·futexwakeup((uint32*)&l->key, 1); 102 } 103 104 // One-time notifications. 105 void 106 runtime·noteclear(Note *n) 107 { 108 n->key = 0; 109 } 110 111 void 112 runtime·notewakeup(Note *n) 113 { 114 if(runtime·xchg((uint32*)&n->key, 1)) 115 runtime·throw("notewakeup - double wakeup"); 116 runtime·futexwakeup((uint32*)&n->key, 1); 117 } 118 119 void 120 runtime·notesleep(Note *n) 121 { 122 if(m->profilehz > 0) 123 runtime·setprof(false); 124 while(runtime·atomicload((uint32*)&n->key) == 0) 125 runtime·futexsleep((uint32*)&n->key, 0, -1); 126 if(m->profilehz > 0) 127 runtime·setprof(true); 128 } 129 130 void 131 runtime·notetsleep(Note *n, int64 ns) 132 { 133 int64 deadline, now; 134 135 if(ns < 0) { 136 runtime·notesleep(n); 137 return; 138 } 139 140 if(runtime·atomicload((uint32*)&n->key) != 0) 141 return; 142 143 if(m->profilehz > 0) 144 runtime·setprof(false); 145 deadline = runtime·nanotime() + ns; 146 for(;;) { 147 runtime·futexsleep((uint32*)&n->key, 0, ns); 148 if(runtime·atomicload((uint32*)&n->key) != 0) 149 break; 150 now = runtime·nanotime(); 151 if(now >= deadline) 152 break; 153 ns = deadline - now; 154 } 155 if(m->profilehz > 0) 156 runtime·setprof(true); 157 }