#include <9pm/u.h>
#include <9pm/libc.h>
#include "dat.h"
#include "fns.h"

struct {
	ulong	locks;
	ulong	glare;
	ulong	inglare;
} lockstats;

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);

//abort();
	if(islo() && up && up->state == Running)
		sched();
}

void
lock(Lock *l)
{
	int i, cansched;
	ulong pc, oldpri;

	pc = getcallerpc(&l);

	lockstats.locks++;
	if(_tas(&l->key) == 0){
		l->pc = pc;
		l->p = up;
		l->isilock = 0;
		return;
	}

	lockstats.glare++;
	cansched = islo() && up != nil && up->state == Running;
	if(cansched){
		oldpri = up->priority;
		up->lockwait = 1;
		up->priority = PriLock;
	} else
		oldpri = 0;

	for(;;){
		lockstats.inglare++;
		i = 0;
		while(l->key){
			if(cansched){
				if(i++ > 1000){
					i = 0;
					lockloop(l, pc);
				}
				sched();
			} else {
				if(i++%1000)
					ossched();
				if(i++ > 100000000){
					i = 0;
					lockloop(l, pc);
				}
			}
		}
		if(_tas(&l->key) == 0){
			l->pc = pc;
			l->p = up;
			l->isilock = 0;
			if(cansched){
				up->lockwait = 0;
				up->priority = oldpri;
			}
			return;
		}
	}
}

int
canlock(Lock *l)
{
	if(_tas(&l->key))
		return 0;

	l->pc = getcallerpc(&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);
	l->pc = 0;
	l->key = 0;
	coherence();
}

