#include <u.h>
#include <libc.h>
#include <thread.h>
#include "threadimpl.h"

enum
{
	STACK = 8192,
};

void
iointerrupt(Ioproc *io)
{
	if(io->ctl < 0)
		return;
	qlock(io);
	if(++io->intr == 1)
		write(io->ctl, "interrupt", 9);
	qunlock(io);
}

static int
openprocctl(void)
{
	char buf[32];

	snprint(buf, sizeof(buf), "/proc/%lud/ctl", (ulong)getpid());
	return open(buf, OWRITE|OCEXEC);
}

static void
xioproc(void *a)
{
	Channel *c;
	Ioproc *io;
	Iocall *r;

	c = a;
	if(io = mallocz(sizeof(*io), 1)){
		/*
		 * open might fail, ignore it for programs like factotum
		 * that don't use iointerrupt() anyway.
		 */
		io->ctl = openprocctl();
		if((io->creply = chancreate(sizeof(void*), 0)) == nil){
			if(io->ctl >= 0)
				close(io->ctl);
			free(io);
			io = nil;
		} else
			io->c = c;
	}
	while(send(c, &io) < 0)
		;
	if(io == nil)
		return;

	for(;;){
		while(recv(io->c, &r) < 0)
			;
		if(r == 0)
			break;
		if(io->intr){
			r->ret = -1;
			strcpy(r->err, "interrupted");
		} else if((r->ret = r->op(&r->arg)) < 0)
			rerrstr(r->err, sizeof r->err);
		qlock(io);
		if(io->intr){
			io->intr = 0;
			if(io->ctl >= 0)
				write(io->ctl, "nointerrupt", 11);
		}
		while(send(io->creply, &r) < 0)
			;
		qunlock(io);
	}

	if(io->ctl >= 0)
		close(io->ctl);
	chanfree(io->c);
	chanfree(io->creply);
	free(io);
}

Ioproc*
ioproc(void)
{
	Channel *c;
	Ioproc *io;

	if((c = chancreate(sizeof(void*), 0)) == nil)
		sysfatal("ioproc chancreate");
	proccreate(xioproc, c, STACK);
	while(recv(c, &io) < 0)
		;
	if(io == nil)
		sysfatal("ioproc alloc");
	return io;
}

void
closeioproc(Ioproc *io)
{
	if(io == nil)
		return;
	iointerrupt(io);
	while(sendp(io->c, nil) < 0)
		;
}
