blob: c8f8b05ce9105b4e92f7aa3f4a68e2e3ab560a8e [file] [log] [blame]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "runtime.h"
enum
{
LOCKED = 1,
ACTIVE_SPIN = 4,
ACTIVE_SPIN_CNT = 30,
PASSIVE_SPIN = 1,
};
// creates per-M semaphore (must not return 0)
uintptr runtime·semacreate(void);
// acquires per-M semaphore
void runtime·semasleep(void);
// releases mp's per-M semaphore
void runtime·semawakeup(M *mp);
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(&l->waitm, 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(&l->waitm);
if((v&LOCKED) == 0) {
unlocked:
if(runtime·casp(&l->waitm, (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(&l->waitm, (void*)v, (void*)((uintptr)m|LOCKED)))
break;
v = (uintptr)runtime·atomicloadp(&l->waitm);
if((v&LOCKED) == 0)
goto unlocked;
}
if(v&LOCKED) {
// Wait.
runtime·semasleep();
i = 0;
}
}
}
}
void
runtime·unlock(Lock *l)
{
uintptr v;
M *mp;
if(--m->locks < 0)
runtime·throw("runtime·unlock: lock count");
for(;;) {
v = (uintptr)runtime·atomicloadp(&l->waitm);
if(v == LOCKED) {
if(runtime·casp(&l->waitm, (void*)LOCKED, nil))
break;
} else {
// Other M's are waiting for the lock.
// Dequeue an M.
mp = (void*)(v&~LOCKED);
if(runtime·casp(&l->waitm, (void*)v, mp->nextwaitm)) {
// Wake that M.
runtime·semawakeup(mp);
break;
}
}
}
}
// One-time notifications.
void
runtime·noteclear(Note *n)
{
n->waitm = nil;
}
void
runtime·notewakeup(Note *n)
{
if(runtime·casp(&n->waitm, nil, (void*)LOCKED))
return;
runtime·semawakeup(n->waitm);
}
void
runtime·notesleep(Note *n)
{
if(m->waitsema == 0)
m->waitsema = runtime·semacreate();
if(runtime·casp(&n->waitm, nil, m))
runtime·semasleep();
}