#include <u.h>#include <libc.h>#include <thread.h>#include "ioproc.h" enum{ STACK = 32768}; voidiointerrupt(Ioproc *io){ if(!io->inuse) return; fprint(2, "bug: cannot iointerrupt %p yet\n", io);} static voidxioproc(void *a){ Ioproc *io, *x; threadsetname("ioproc"); io = a; /* * first recvp acquires the ioproc. * second tells us that the data is ready. */ for(;;){ while(recv(io->c, &x) == -1) ; if(x == 0) /* our cue to leave */ break; assert(x == io); /* caller is now committed -- even if interrupted he'll return */ while(recv(io->creply, &x) == -1) ; if(x == 0) /* caller backed out */ continue; assert(x == io); io->ret = io->op(&io->arg); if(io->ret < 0) rerrstr(io->err, sizeof io->err); while(send(io->creply, &io) == -1) ; while(recv(io->creply, &x) == -1) ; }} Ioproc*ioproc(void){ Ioproc *io; io = mallocz(sizeof(*io), 1); if(io == nil) sysfatal("ioproc malloc: %r"); io->c = chancreate(sizeof(void*), 0); chansetname(io->c, "ioc%p", io->c); io->creply = chancreate(sizeof(void*), 0); chansetname(io->creply, "ior%p", io->c); io->tid = proccreate(xioproc, io, STACK); return io;} voidcloseioproc(Ioproc *io){ if(io == nil) return; iointerrupt(io); while(send(io->c, 0) == -1) ; chanfree(io->c); chanfree(io->creply); free(io);} longiocall(Ioproc *io, long (*op)(va_list*), ...){ char e[ERRMAX]; int ret, inted; Ioproc *msg; if(send(io->c, &io) == -1){ werrstr("interrupted"); return -1; } assert(!io->inuse); io->inuse = 1; io->op = op; va_start(io->arg, op); msg = io; inted = 0; while(send(io->creply, &msg) == -1){ msg = nil; inted = 1; } if(inted){ werrstr("interrupted"); return -1; } /* * If we get interrupted, we have stick around so that * the IO proc has someone to talk to. Send it an interrupt * and try again. */ inted = 0; while(recv(io->creply, nil) == -1){ inted = 1; iointerrupt(io); } USED(inted); va_end(io->arg); ret = io->ret; if(ret < 0) strecpy(e, e+sizeof e, io->err); io->inuse = 0; /* release resources */ while(send(io->creply, &io) == -1) ; if(ret < 0) errstr(e, sizeof e); return ret;}
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "ioproc.h"
enum
{
STACK = 32768
};
void
iointerrupt(Ioproc *io)
{
if(!io->inuse)
return;
fprint(2, "bug: cannot iointerrupt %p yet\n", io);
}
static void
xioproc(void *a)
{
Ioproc *io, *x;
threadsetname("ioproc");
io = a;
/*
* first recvp acquires the ioproc.
* second tells us that the data is ready.
*/
for(;;){
while(recv(io->c, &x) == -1)
;
if(x == 0) /* our cue to leave */
break;
assert(x == io);
/* caller is now committed -- even if interrupted he'll return */
while(recv(io->creply, &x) == -1)
;
if(x == 0) /* caller backed out */
continue;
assert(x == io);
io->ret = io->op(&io->arg);
if(io->ret < 0)
rerrstr(io->err, sizeof io->err);
while(send(io->creply, &io) == -1)
;
while(recv(io->creply, &x) == -1)
;
}
}
Ioproc*
ioproc(void)
{
Ioproc *io;
io = mallocz(sizeof(*io), 1);
if(io == nil)
sysfatal("ioproc malloc: %r");
io->c = chancreate(sizeof(void*), 0);
chansetname(io->c, "ioc%p", io->c);
io->creply = chancreate(sizeof(void*), 0);
chansetname(io->creply, "ior%p", io->c);
io->tid = proccreate(xioproc, io, STACK);
return io;
}
void
closeioproc(Ioproc *io)
{
if(io == nil)
return;
iointerrupt(io);
while(send(io->c, 0) == -1)
;
chanfree(io->c);
chanfree(io->creply);
free(io);
}
long
iocall(Ioproc *io, long (*op)(va_list*), ...)
{
char e[ERRMAX];
int ret, inted;
Ioproc *msg;
if(send(io->c, &io) == -1){
werrstr("interrupted");
return -1;
}
assert(!io->inuse);
io->inuse = 1;
io->op = op;
va_start(io->arg, op);
msg = io;
inted = 0;
while(send(io->creply, &msg) == -1){
msg = nil;
inted = 1;
}
if(inted){
werrstr("interrupted");
return -1;
}
/*
* If we get interrupted, we have stick around so that
* the IO proc has someone to talk to. Send it an interrupt
* and try again.
*/
inted = 0;
while(recv(io->creply, nil) == -1){
inted = 1;
iointerrupt(io);
}
USED(inted);
va_end(io->arg);
ret = io->ret;
if(ret < 0)
strecpy(e, e+sizeof e, io->err);
io->inuse = 0;
/* release resources */
while(send(io->creply, &io) == -1)
;
if(ret < 0)
errstr(e, sizeof e);
return ret;
}