#include        "u.h"
#include        "lib.h"
#include        "dat.h"
#include        "fns.h"
#include        "io.h"
#include        "mem.h"

void
lock(Lock *l)
{
       int i;

       /*
        * Try the fast grab first
        */
loop:
       if(tas(l) == 0)
               return;
       for(i = 0; i < 1000000; i++) {
               if(tas(l) == 0)
                       return;
               /* If we are spl low resched */
               if(getstatus() & IFLAG)
                       sched();
       }
       l->sbsem = 0;

       print("lock loop 0x%lux held by pc 0x%lux\n", (ulong)l, l->pc);
       goto loop;
}

void
ilock(Lock *l)
{
       l->sr = splhi();
       lock(l);
}

void
iunlock(Lock *l)
{
       ulong sr;

       sr = l->sr;
       l->sbsem = 0;
       l->pc = 0;
       splx(sr);
}

int
canlock(Lock *l)
{
       if (tas(l) == 0)
               return 1;
       return 0;
}

void
unlock(Lock *l)
{
       l->pc = 0;
       l->sbsem = 0;
}

void
qlock(QLock *q)
{
       User *p;
       int i;

       lock(q);
       if(!q->locked){
               q->locked = 1;
               unlock(q);
               goto out;
       }
       p = q->tail;
       if(p == 0)
               q->head = u;
       else
               p->qnext = u;
       q->tail = u;
       u->qnext = 0;
       u->state = Queueing;
       u->has.want = q;
       unlock(q);
       sched();
       u->has.want = 0;

out:
       if(1 && u) {
               for(i=0; i<NHAS; i++)
                       if(u->has.q[i] == 0) {
                               u->has.q[i] = q;
                               return;
                       }
               print("NHAS(%d) too small\n", NHAS);
       }
}

int
canqlock(QLock *q)
{
       int i;

       lock(q);
       if(q->locked){
               unlock(q);
               return 0;
       }
       q->locked = 1;
       unlock(q);

       if(1 && u) {
               for(i=0; i<NHAS; i++)
                       if(u->has.q[i] == 0) {
                               u->has.q[i] = q;
                               return 1;
                       }
               print("NHAS(%d) too small\n", NHAS);
       }
       return 1;
}

void
qunlock(QLock *q)
{
       User *p;
       int i;

       lock(q);
       p = q->head;
       if(p) {
               q->head = p->qnext;
               if(q->head == 0)
                       q->tail = 0;
               unlock(q);
               ready(p);
       } else {
               q->locked = 0;
               unlock(q);
       }

       if(1 && u) {
               for(i=0; i<NHAS; i++)
                       if(u->has.q[i] == q) {
                               u->has.q[i] = 0;
                               return;
                       }
               panic("qunlock: not there %lux, called from %lux\n",
                       (ulong)q, getcallerpc(&q));
       }
}

/*
* readers/writers lock
* allows 1 writer or many readers
*/
void
rlock(RWlock *l)
{
       QLock *q;

       qlock(&l->wr);                  /* wait here for writers and exclusion */

       q = &l->rd;                     /* first reader in, qlock(&l->rd) */
       lock(q);
       q->locked = 1;
       l->nread++;
       unlock(q);

       qunlock(&l->wr);
}

void
runlock(RWlock *l)
{
       QLock *q;
       User *p;
       int n;

       q = &l->rd;
       lock(q);
       n = l->nread - 1;
       l->nread = n;
       if(n == 0) {                    /* last reader out, qunlock(&l->rd) */
               p = q->head;
               if(p) {
                       q->head = p->qnext;
                       if(q->head == 0)
                               q->tail = 0;
                       unlock(q);
                       ready(p);
                       return;
               }
               q->locked = 0;
       }
       unlock(q);
}

void
wlock(RWlock *l)
{
       qlock(&l->wr);                  /* wait here for writers and exclusion */
       qlock(&l->rd);                  /* wait here for last reader */
}

void
wunlock(RWlock *l)
{
       qunlock(&l->rd);
       qunlock(&l->wr);
}