#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
/*
* The hardware semaphores are strange. 64 per page, replicated 16 times
* per page, 1024 pages of them. Only the low bit is meaningful.
* Reading an unset semaphore sets the semaphore and returns the old value.
* Writing a semaphore sets the value, so writing 0 resets (clears) the semaphore.
*/
#define SEMPERPG 64 /* hardware semaphores per page */
#define NONSEMPERPG (WD2PG-64) /* words of non-semaphore per page */
struct
{
Lock lock; /* lock to allocate */
ulong *nextsem; /* next one to allocate */
int nsem; /* at SEMPERPG, jump to next page */
}semalloc;
void
lockinit(void)
{
semalloc.lock.sbsem = SBSEM;
semalloc.nextsem = SBSEM+1;
semalloc.nsem = 1;
unlock(&semalloc.lock);
}
#define PCOFF -9
/*
* If l->sbsem is zero, allocate a hardware semaphore first.
* There is no way to free a semaphore.
*/
void
lock(Lock *ll)
{
Lock *l = ll;
int i;
ulong *sbsem;
sbsem = l->sbsem;
if(sbsem == 0){
lock(&semalloc.lock);
if(semalloc.nsem == SEMPERPG){
semalloc.nsem = 0;
semalloc.nextsem += NONSEMPERPG;
if(semalloc.nextsem == SBSEMTOP)
panic("sem");
}
l->sbsem = semalloc.nextsem;
semalloc.nextsem++;
semalloc.nsem++;
unlock(&semalloc.lock);
unlock(l); /* put sem in known state */
sbsem = l->sbsem;
}
/*
* Try the fast grab first
*/
if((*sbsem&1) == 0){
l->pc = ((ulong*)&ll)[PCOFF];
return;
}
for(i=0; i<10000000; i++)
if((*sbsem&1) == 0){
l->pc = ((ulong*)&ll)[PCOFF];
return;
}
*sbsem = 0;
print("lock loop %lux pc %lux held by pc %lux\n", l, ((ulong*)&ll)[PCOFF], l->pc);
dumpstack();
}
int
canlock(Lock *l)
{
ulong *sbsem;
sbsem = l->sbsem;
if(sbsem == 0){
lock(&semalloc.lock);
if(semalloc.nsem == SEMPERPG){
semalloc.nsem = 0;
semalloc.nextsem += NONSEMPERPG;
if(semalloc.nextsem == SBSEMTOP)
panic("sem");
}
l->sbsem = semalloc.nextsem;
semalloc.nextsem++;
semalloc.nsem++;
unlock(&semalloc.lock);
unlock(l); /* put sem in known state */
sbsem = l->sbsem;
}
if(*sbsem & 1)
return 0;
return 1;
}
void
unlock(Lock *l)
{
l->pc = 0;
*l->sbsem = 0;
}