|  | #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; | 
|  | } |