1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "ioproc.h"
5 
6 enum
7 {
8 	STACK = 32768
9 };
10 
11 void
iointerrupt(Ioproc * io)12 iointerrupt(Ioproc *io)
13 {
14 	if(!io->inuse)
15 		return;
16 	fprint(2, "bug: cannot iointerrupt %p yet\n", io);
17 }
18 
19 static void
xioproc(void * a)20 xioproc(void *a)
21 {
22 	Ioproc *io, *x;
23 
24 	threadsetname("ioproc");
25 	io = a;
26 	/*
27 	 * first recvp acquires the ioproc.
28 	 * second tells us that the data is ready.
29 	 */
30 	for(;;){
31 		while(recv(io->c, &x) == -1)
32 			;
33 		if(x == 0)	/* our cue to leave */
34 			break;
35 		assert(x == io);
36 
37 		/* caller is now committed -- even if interrupted he'll return */
38 		while(recv(io->creply, &x) == -1)
39 			;
40 		if(x == 0)	/* caller backed out */
41 			continue;
42 		assert(x == io);
43 
44 		io->ret = io->op(&io->arg);
45 		if(io->ret < 0)
46 			rerrstr(io->err, sizeof io->err);
47 		while(send(io->creply, &io) == -1)
48 			;
49 		while(recv(io->creply, &x) == -1)
50 			;
51 	}
52 }
53 
54 Ioproc*
ioproc(void)55 ioproc(void)
56 {
57 	Ioproc *io;
58 
59 	io = mallocz(sizeof(*io), 1);
60 	if(io == nil)
61 		sysfatal("ioproc malloc: %r");
62 	io->c = chancreate(sizeof(void*), 0);
63 	chansetname(io->c, "ioc%p", io->c);
64 	io->creply = chancreate(sizeof(void*), 0);
65 	chansetname(io->creply, "ior%p", io->c);
66 	io->tid = proccreate(xioproc, io, STACK);
67 	return io;
68 }
69 
70 void
closeioproc(Ioproc * io)71 closeioproc(Ioproc *io)
72 {
73 	if(io == nil)
74 		return;
75 	iointerrupt(io);
76 	while(send(io->c, 0) == -1)
77 		;
78 	chanfree(io->c);
79 	chanfree(io->creply);
80 	free(io);
81 }
82 
83 long
iocall(Ioproc * io,long (* op)(va_list *),...)84 iocall(Ioproc *io, long (*op)(va_list*), ...)
85 {
86 	char e[ERRMAX];
87 	int ret, inted;
88 	Ioproc *msg;
89 
90 	if(send(io->c, &io) == -1){
91 		werrstr("interrupted");
92 		return -1;
93 	}
94 	assert(!io->inuse);
95 	io->inuse = 1;
96 	io->op = op;
97 	va_start(io->arg, op);
98 	msg = io;
99 	inted = 0;
100 	while(send(io->creply, &msg) == -1){
101 		msg = nil;
102 		inted = 1;
103 	}
104 	if(inted){
105 		werrstr("interrupted");
106 		return -1;
107 	}
108 
109 	/*
110 	 * If we get interrupted, we have stick around so that
111 	 * the IO proc has someone to talk to.  Send it an interrupt
112 	 * and try again.
113 	 */
114 	inted = 0;
115 	while(recv(io->creply, nil) == -1){
116 		inted = 1;
117 		iointerrupt(io);
118 	}
119 	USED(inted);
120 	va_end(io->arg);
121 	ret = io->ret;
122 	if(ret < 0)
123 		strecpy(e, e+sizeof e, io->err);
124 	io->inuse = 0;
125 
126 	/* release resources */
127 	while(send(io->creply, &io) == -1)
128 		;
129 	if(ret < 0)
130 		errstr(e, sizeof e);
131 	return ret;
132 }
133