void runtime·lock(Lock *l) { uintptr v; uint32 i, spin; if(m->locks++ < 0) runtime·throw("runtime·lock: lock count"); // Speculative grab for lock. if(runtime·casp((void**)&l->key, nil, (void*)LOCKED)) return; if(m->waitsema == 0) m->waitsema = runtime·semacreate(); // On uniprocessor's, no point spinning. // On multiprocessors, spin for ACTIVE_SPIN attempts. spin = 0; if(runtime·ncpu > 1) spin = ACTIVE_SPIN; for(i=0;; i++) { v = (uintptr)runtime·atomicloadp((void**)&l->key); if((v&LOCKED) == 0) { unlocked: if(runtime·casp((void**)&l->key, (void*)v, (void*)(v|LOCKED))) return; i = 0; } if(i<spin) runtime·procyield(ACTIVE_SPIN_CNT); else if(i<spin+PASSIVE_SPIN) runtime·osyield(); else { // Someone else has it. // l->waitm points to a linked list of M's waiting // for this lock, chained through m->nextwaitm. // Queue this M. for(;;) { m->nextwaitm = (void*)(v&~LOCKED); if(runtime·casp((void**)&l->key, (void*)v, (void*)((uintptr)m|LOCKED))) break; v = (uintptr)runtime·atomicloadp((void**)&l->key); if((v&LOCKED) == 0) goto unlocked; } if(v&LOCKED) { // Queued. Wait. runtime·semasleep(-1); i = 0; } } } }
// Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING. // MUTEX_SLEEPING means that there is presumably at least one sleeping thread. // Note that there can be spinning threads during all states - they do not // affect mutex's state. void runtime·lock(Lock *l) { uint32 i, v, wait, spin; if(m->locks++ < 0) runtime·throw("runtime·lock: lock count"); // Speculative grab for lock. v = runtime·xchg(&l->key, MUTEX_LOCKED); if(v == MUTEX_UNLOCKED) return; // wait is either MUTEX_LOCKED or MUTEX_SLEEPING // depending on whether there is a thread sleeping // on this mutex. If we ever change l->key from // MUTEX_SLEEPING to some other value, we must be // careful to change it back to MUTEX_SLEEPING before // returning, to ensure that the sleeping thread gets // its wakeup call. wait = v; // On uniprocessor's, no point spinning. // On multiprocessors, spin for ACTIVE_SPIN attempts. spin = 0; if(runtime·ncpu > 1) spin = ACTIVE_SPIN; for(;;) { // Try for lock, spinning. for(i = 0; i < spin; i++) { while(l->key == MUTEX_UNLOCKED) if(runtime·cas(&l->key, MUTEX_UNLOCKED, wait)) return; runtime·procyield(ACTIVE_SPIN_CNT); } // Try for lock, rescheduling. for(i=0; i < PASSIVE_SPIN; i++) { while(l->key == MUTEX_UNLOCKED) if(runtime·cas(&l->key, MUTEX_UNLOCKED, wait)) return; runtime·osyield(); } // Sleep. v = runtime·xchg(&l->key, MUTEX_SLEEPING); if(v == MUTEX_UNLOCKED) return; wait = MUTEX_SLEEPING; runtime·futexsleep(&l->key, MUTEX_SLEEPING, -1); } }