#include	"dat.h"
#include	"fns.h"
#include	"error.h"
#include	"interp.h"

Proc*
newproc(void)
{
	Proc *p;

	p = malloc(sizeof(Proc));
	if(p == nil)
		return nil;

	p->type = Unknown;
	p->killed = 0;
	p->swipend = 0;
	p->env = &p->defenv;
	kstrdup(&p->env->user, "*nouser");
	p->env->errstr = p->env->errbuf0;
	p->env->syserrstr = p->env->errbuf1;
	addprog(p);

	return p;
}

void
Sleep(Rendez *r, int (*f)(void*), void *arg)
{
	lock(&up->rlock);
	lock(&r->l);

	/*
	 * if interrupted or condition happened, never mind
	 */
	if(up->killed || f(arg)) {
		unlock(&r->l);
	}else{
		if(r->p != nil)
			panic("double sleep pc=0x%lux %s[%lud] %s[%lud] r=0x%lux\n", getcallerpc(&r), r->p->text, r->p->pid, up->text, up->pid, r);

		r->p = up;
		unlock(&r->l);
		up->swipend = 0;
		up->r = r;	/* for swiproc */
		unlock(&up->rlock);

		osblock();

		lock(&up->rlock);
		up->r = nil;
	}

	if(up->killed || up->swipend) {
		up->killed = 0;
		up->swipend = 0;
		unlock(&up->rlock);
		error(Eintr);
	}
	unlock(&up->rlock);
}

int
Wakeup(Rendez *r)
{
	Proc *p;

	lock(&r->l);
	p = r->p;
	if(p != nil) {
		r->p = nil;
		osready(p);
	}
	unlock(&r->l);
	return p != nil;
}

void
swiproc(Proc *p, int interp)
{
	Rendez *r;
	
	if(p == nil)
		return;

	/*
	 * Pull out of emu Sleep
	 */
	lock(&p->rlock);
	if(!interp)
		p->killed = 1;
	r = p->r;
	if(r != nil) {
		lock(&r->l);
		if(r->p == p){
			p->swipend = 1;
			r->p = nil;
			osready(p);
		}
		unlock(&r->l);
		unlock(&p->rlock);
		return;
	}
	unlock(&p->rlock);

	/*
	 * Maybe pull out of Host OS
	 */
	lock(&p->sysio);
	if(p->syscall && p->intwait == 0) {
		p->swipend = 1;
		p->intwait = 1;
		unlock(&p->sysio);
		oshostintr(p);
		return;	
	}
	unlock(&p->sysio);
}

void
notkilled(void)
{
	lock(&up->rlock);
	up->killed = 0;
	unlock(&up->rlock);
}

void
osenter(void)
{
	up->syscall = 1;
}

void
osleave(void)
{
	int r;

	lock(&up->rlock);
	r = up->swipend;
	up->swipend = 0;
	unlock(&up->rlock);

	lock(&up->sysio);
	up->syscall = 0;
	unlock(&up->sysio);

	/* Cleared by the signal/note/exception handler */
	while(up->intwait)
		osyield();

	if(r != 0)
		error(Eintr);
}

void
rptwakeup(void *o, void *ar)
{
	Rept *r;

	r = ar;
	if(r == nil)
		return;
	lock(&r->l);
	r->o = o;
	unlock(&r->l);
	Wakeup(&r->r);
}

static int
rptactive(void *a)
{
	Rept *r = a;
	int i;
	lock(&r->l);
	i = r->active(r->o);
	unlock(&r->l);
	return i;
}

static void
rproc(void *a)
{
	long now, then;
	ulong t;
	int i;
	void *o;
	Rept *r;

	up->env->fgrp = newfgrp(nil);
	r = a;
	t = (ulong)r->t;

Wait:
	Sleep(&r->r, rptactive, r);
	lock(&r->l);
	o = r->o;
	unlock(&r->l);
	then = osmillisec();
	for(;;){
		osmillisleep(t);
		now = osmillisec();
		if(waserror())
			break;
		i = r->ck(o, now-then);
		poperror();
		if(i == -1)
			goto Wait;
		if(i == 0)
			continue;
		then = now;
		acquire();
		if(waserror()) {
			release();
			break;
		}
		r->f(o);
		poperror();
		release();
	}
	pexit("", 0);
}

void*
rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*))
{
	Rept *r;

	r = mallocz(sizeof(Rept), 1);
	if(r == nil)
		return nil;
	r->t = t;
	r->active = active;
	r->ck = ck;
	r->f = f;
	r->o = o;
	kproc(s, rproc, r, KPDUPPG);
	return r;
}
