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