#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
struct {
ulong locks;
ulong glare;
ulong inglare;
} lockstats;
typedef struct PC {
ulong pc;
int count;
} PC;
PC lpcs[1024];
void
incpcref(Lock *l)
{
int i;
for(i = 0; i < nelem(lpcs)-1; i++){
if(lpcs[i].pc == l->pc)
break;
if(lpcs[i].pc == 0){
lpcs[i].pc = l->pc;
break;
}
}
lpcs[i].count++;
}
void
decpcref(Lock *l)
{
int i;
for(i = 0; i < nelem(lpcs)-1; i++){
if(lpcs[i].pc == l->pc)
break;
if(lpcs[i].pc == 0){
lpcs[i].pc = l->pc;
break;
}
}
lpcs[i].count--;
}
static void
dumplockmem(char *tag, Lock *l)
{
uchar *cp;
int i;
iprint("%s: ", tag);
cp = (uchar*)l;
for(i = 0; i < 64; i++)
iprint("%2.2ux ", cp[i]);
iprint("\n");
}
void
lockloop(Lock *l, ulong pc)
{
Proc *p;
p = l->p;
print("lock loop %p\n", l);
delay(1000);
print("lock 0x%lux loop key 0x%lux pc 0x%lux held by pc 0x%lux proc %lud\n",
l, l->key, pc, l->pc, p ? p->pid : 0);
dumpaproc(up);
if(p != nil)
dumpaproc(p);
if(up && up->state == Running && islo())
sched();
}
void
lock(Lock *l)
{
int i, cansched;
ulong pc, oldpri;
pc = getcallerpc(&l);
lockstats.locks++; /* prevent being scheded */
if (up) up->nlocks++;
if(tas(&l->key) == 0){
if (up) up->lastlock = l;
l->pc = pc;
// incpcref(l);
l->p = up;
l->isilock = 0;
return;
}
if (up) up->nlocks--; /* didn't get the lock, allow scheding */
lockstats.glare++;
cansched = up != nil && up->state == Running;
if(cansched){
oldpri = up->priority;
up->priority = PriLock;
up->lockwait = l;
} else
oldpri = 0;
for(;;){
lockstats.inglare++;
i = 0;
while(l->key){
if(conf.nmach < 2 && cansched){
if (i++ > 1000){
i = 0;
lockloop(l, pc);
}
sched();
} else {
if(i++ > 100000000){
i = 0;
lockloop(l, pc);
}
}
}
if (up) up->nlocks++;
if(tas(&l->key) == 0){
if (up) up->lastlock = l;
l->pc = pc;
// incpcref(l);
l->p = up;
l->isilock = 0;
if(cansched){
up->lockwait = nil;
up->priority = oldpri;
}
return;
}
if (up) up->nlocks--;
}
}
void
ilock(Lock *l)
{
ulong x;
ulong pc, oldpri;
int cansched;
pc = getcallerpc(&l);
lockstats.locks++;
x = splhi();
if(tas(&l->key) == 0){
if (up) up->lastlock = l;
l->sr = x;
l->pc = pc;
l->p = up;
l->isilock = 1;
return;
}
lockstats.glare++;
cansched = up != nil && up->state == Running;
if(cansched){
oldpri = up->priority;
up->lockwait = l;
up->priority = PriLock;
} else
oldpri = 0;
if(conf.nmach < 2){
dumplockmem("ilock:", l);
panic("ilock: no way out: pc %luX\n", pc);
}
for(;;){
lockstats.inglare++;
splx(x);
while(l->key)
;
x = splhi();
if(tas(&l->key) == 0){
if (up) up->lastlock = l;
l->sr = x;
l->pc = pc;
l->p = up;
l->isilock = 1;
if(cansched){
up->lockwait = nil;
up->priority = oldpri;
}
return;
}
}
}
int
canlock(Lock *l)
{
if (up) up->nlocks++;
if(tas(&l->key)){
if (up) up->nlocks--;
return 0;
}
if (up) up->lastlock = l;
l->pc = getcallerpc(&l);
// incpcref(l);
l->p = up;
l->isilock = 0;
return 1;
}
void
unlock(Lock *l)
{
if(l->key == 0)
print("unlock: not locked: pc %luX\n", getcallerpc(&l));
if(l->isilock)
print("unlock of ilock: pc %lux, held by %lux\n", getcallerpc(&l), l->pc);
if(l->p != up)
print("unlock: up changed: pc %lux, acquired at pc %lux, lock p 0x%p, unlock up 0x%p\n", getcallerpc(&l), l->pc, l->p, up);
// decpcref(l);
l->pc = 0;
l->key = 0;
if (up && --up->nlocks == 0 && up->delaysched){
up->delaysched = 0;
sched();
}
coherence();
}
void
iunlock(Lock *l)
{
ulong sr;
if(l->key == 0)
print("iunlock: not locked: pc %luX\n", getcallerpc(&l));
if(!l->isilock)
print("iunlock of lock: pc %lux, held by %lux\n", getcallerpc(&l), l->pc);
sr = l->sr;
l->pc = 0;
l->key = 0;
coherence();
m->splpc = getcallerpc(&l);
splxpc(sr);
}