1 76193d7c 2003-09-30 devnull #include "threadimpl.h"
3 76193d7c 2003-09-30 devnull static Lock chanlock; /* central channel access lock */
5 76193d7c 2003-09-30 devnull static void enqueue(Alt*, Channel**);
6 76193d7c 2003-09-30 devnull static void dequeue(Alt*);
7 76193d7c 2003-09-30 devnull static int altexec(Alt*, int);
9 76193d7c 2003-09-30 devnull int _threadhighnentry;
10 76193d7c 2003-09-30 devnull int _threadnalt;
12 02a1a5c1 2004-03-05 devnull static void
13 02a1a5c1 2004-03-05 devnull setuserpc(ulong pc)
15 02a1a5c1 2004-03-05 devnull Thread *t;
17 02a1a5c1 2004-03-05 devnull t = _threadgetproc()->thread;
19 02a1a5c1 2004-03-05 devnull t->userpc = pc;
22 76193d7c 2003-09-30 devnull static int
23 76193d7c 2003-09-30 devnull canexec(Alt *a)
25 76193d7c 2003-09-30 devnull int i, otherop;
26 76193d7c 2003-09-30 devnull Channel *c;
28 76193d7c 2003-09-30 devnull c = a->c;
29 76193d7c 2003-09-30 devnull /* are there senders or receivers blocked? */
30 76193d7c 2003-09-30 devnull otherop = (CHANSND+CHANRCV) - a->op;
31 76193d7c 2003-09-30 devnull for(i=0; i<c->nentry; i++)
32 76193d7c 2003-09-30 devnull if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil){
33 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, c);
34 76193d7c 2003-09-30 devnull return 1;
37 76193d7c 2003-09-30 devnull /* is there room in the channel? */
38 76193d7c 2003-09-30 devnull if((a->op==CHANSND && c->n < c->s)
39 76193d7c 2003-09-30 devnull || (a->op==CHANRCV && c->n > 0)){
40 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "can buffer alt %p chan %p", a, c);
41 76193d7c 2003-09-30 devnull return 1;
44 76193d7c 2003-09-30 devnull return 0;
47 76193d7c 2003-09-30 devnull static void
48 76193d7c 2003-09-30 devnull _chanfree(Channel *c)
50 76193d7c 2003-09-30 devnull int i, inuse;
52 76193d7c 2003-09-30 devnull inuse = 0;
53 76193d7c 2003-09-30 devnull for(i = 0; i < c->nentry; i++)
54 76193d7c 2003-09-30 devnull if(c->qentry[i])
55 76193d7c 2003-09-30 devnull inuse = 1;
56 76193d7c 2003-09-30 devnull if(inuse)
57 76193d7c 2003-09-30 devnull c->freed = 1;
59 76193d7c 2003-09-30 devnull if(c->qentry)
60 76193d7c 2003-09-30 devnull free(c->qentry);
66 76193d7c 2003-09-30 devnull chanfree(Channel *c)
68 76193d7c 2003-09-30 devnull lock(&chanlock);
69 76193d7c 2003-09-30 devnull _chanfree(c);
70 76193d7c 2003-09-30 devnull unlock(&chanlock);
74 76193d7c 2003-09-30 devnull chaninit(Channel *c, int elemsize, int elemcnt)
76 76193d7c 2003-09-30 devnull if(elemcnt < 0 || elemsize <= 0 || c == nil)
77 76193d7c 2003-09-30 devnull return -1;
78 5a8e63b2 2004-02-29 devnull memset(c, 0, sizeof *c);
79 76193d7c 2003-09-30 devnull c->e = elemsize;
80 76193d7c 2003-09-30 devnull c->s = elemcnt;
81 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "chaninit %p", c);
82 76193d7c 2003-09-30 devnull return 1;
86 76193d7c 2003-09-30 devnull chancreate(int elemsize, int elemcnt)
88 76193d7c 2003-09-30 devnull Channel *c;
90 76193d7c 2003-09-30 devnull if(elemcnt < 0 || elemsize <= 0)
91 76193d7c 2003-09-30 devnull return nil;
92 76193d7c 2003-09-30 devnull c = _threadmalloc(sizeof(Channel)+elemsize*elemcnt, 1);
93 76193d7c 2003-09-30 devnull c->e = elemsize;
94 76193d7c 2003-09-30 devnull c->s = elemcnt;
95 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "chancreate %p", c);
96 76193d7c 2003-09-30 devnull return c;
99 02a1a5c1 2004-03-05 devnull static int
100 02a1a5c1 2004-03-05 devnull _alt(Alt *alts)
102 76193d7c 2003-09-30 devnull Alt *a, *xa;
103 76193d7c 2003-09-30 devnull Channel *volatile c;
104 76193d7c 2003-09-30 devnull int n, s;
105 76193d7c 2003-09-30 devnull ulong r;
106 76193d7c 2003-09-30 devnull Thread *t;
109 76193d7c 2003-09-30 devnull * The point of going splhi here is that note handlers
110 76193d7c 2003-09-30 devnull * might reasonably want to use channel operations,
111 76193d7c 2003-09-30 devnull * but that will hang if the note comes while we hold the
112 76193d7c 2003-09-30 devnull * chanlock. Instead, we delay the note until we've dropped
113 76193d7c 2003-09-30 devnull * the lock.
117 5a8e63b2 2004-02-29 devnull * T might be nil here -- the scheduler sends on threadwaitchan
118 5a8e63b2 2004-02-29 devnull * directly (in non-blocking mode, of course!).
120 76193d7c 2003-09-30 devnull t = _threadgetproc()->thread;
121 5a8e63b2 2004-02-29 devnull if((t && t->moribund) || _threadexitsallstatus)
122 76193d7c 2003-09-30 devnull yield(); /* won't return */
123 76193d7c 2003-09-30 devnull s = _procsplhi();
124 76193d7c 2003-09-30 devnull lock(&chanlock);
126 76193d7c 2003-09-30 devnull /* test whether any channels can proceed */
128 76193d7c 2003-09-30 devnull a = nil;
130 76193d7c 2003-09-30 devnull for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){
131 76193d7c 2003-09-30 devnull xa->entryno = -1;
132 76193d7c 2003-09-30 devnull if(xa->op == CHANNOP)
133 76193d7c 2003-09-30 devnull continue;
135 76193d7c 2003-09-30 devnull c = xa->c;
136 76193d7c 2003-09-30 devnull if(c==nil){
137 76193d7c 2003-09-30 devnull unlock(&chanlock);
138 76193d7c 2003-09-30 devnull _procsplx(s);
139 76193d7c 2003-09-30 devnull return -1;
141 76193d7c 2003-09-30 devnull if(canexec(xa))
142 76193d7c 2003-09-30 devnull if(nrand(++n) == 0)
146 76193d7c 2003-09-30 devnull if(a==nil){
147 76193d7c 2003-09-30 devnull /* nothing can proceed */
148 76193d7c 2003-09-30 devnull if(xa->op == CHANNOBLK){
149 76193d7c 2003-09-30 devnull unlock(&chanlock);
150 76193d7c 2003-09-30 devnull _procsplx(s);
151 76193d7c 2003-09-30 devnull _threadnalt++;
152 76193d7c 2003-09-30 devnull return xa - alts;
155 76193d7c 2003-09-30 devnull /* enqueue on all channels. */
156 76193d7c 2003-09-30 devnull c = nil;
157 76193d7c 2003-09-30 devnull for(xa=alts; xa->op!=CHANEND; xa++){
158 76193d7c 2003-09-30 devnull if(xa->op==CHANNOP)
159 76193d7c 2003-09-30 devnull continue;
160 76193d7c 2003-09-30 devnull enqueue(xa, (Channel**)&c);
164 76193d7c 2003-09-30 devnull * wait for successful rendezvous.
165 76193d7c 2003-09-30 devnull * we can't just give up if the rendezvous
166 76193d7c 2003-09-30 devnull * is interrupted -- someone else might come
167 76193d7c 2003-09-30 devnull * along and try to rendezvous with us, so
168 76193d7c 2003-09-30 devnull * we need to be here.
171 5a8e63b2 2004-02-29 devnull t->alt = alts;
172 5a8e63b2 2004-02-29 devnull t->chan = Chanalt;
174 76193d7c 2003-09-30 devnull unlock(&chanlock);
175 76193d7c 2003-09-30 devnull _procsplx(s);
176 76193d7c 2003-09-30 devnull r = _threadrendezvous((ulong)&c, 0);
177 76193d7c 2003-09-30 devnull s = _procsplhi();
178 76193d7c 2003-09-30 devnull lock(&chanlock);
180 76193d7c 2003-09-30 devnull if(r==~0){ /* interrupted */
181 76193d7c 2003-09-30 devnull if(c!=nil) /* someone will meet us; go back */
182 76193d7c 2003-09-30 devnull goto Again;
183 76193d7c 2003-09-30 devnull c = (Channel*)~0; /* so no one tries to meet us */
186 76193d7c 2003-09-30 devnull /* dequeue from channels, find selected one */
187 76193d7c 2003-09-30 devnull a = nil;
188 76193d7c 2003-09-30 devnull for(xa=alts; xa->op!=CHANEND; xa++){
189 76193d7c 2003-09-30 devnull if(xa->op==CHANNOP)
190 76193d7c 2003-09-30 devnull continue;
191 76193d7c 2003-09-30 devnull if(xa->c == c)
193 76193d7c 2003-09-30 devnull dequeue(xa);
195 76193d7c 2003-09-30 devnull unlock(&chanlock);
196 76193d7c 2003-09-30 devnull _procsplx(s);
197 76193d7c 2003-09-30 devnull if(a == nil){ /* we were interrupted */
198 76193d7c 2003-09-30 devnull assert(c==(Channel*)~0);
199 76193d7c 2003-09-30 devnull return -1;
202 76193d7c 2003-09-30 devnull altexec(a, s); /* unlocks chanlock, does splx */
205 8c6f0e8a 2004-03-05 devnull t->chan = Channone;
206 76193d7c 2003-09-30 devnull return a - alts;
210 02a1a5c1 2004-03-05 devnull alt(Alt *alts)
212 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&alts));
213 02a1a5c1 2004-03-05 devnull return _alt(alts);
216 76193d7c 2003-09-30 devnull static int
217 76193d7c 2003-09-30 devnull runop(int op, Channel *c, void *v, int nb)
220 76193d7c 2003-09-30 devnull Alt a[2];
223 76193d7c 2003-09-30 devnull * we could do this without calling alt,
224 76193d7c 2003-09-30 devnull * but the only reason would be performance,
225 76193d7c 2003-09-30 devnull * and i'm not convinced it matters.
227 76193d7c 2003-09-30 devnull a[0].op = op;
228 76193d7c 2003-09-30 devnull a[0].c = c;
229 76193d7c 2003-09-30 devnull a[0].v = v;
230 76193d7c 2003-09-30 devnull a[1].op = CHANEND;
232 76193d7c 2003-09-30 devnull a[1].op = CHANNOBLK;
233 02a1a5c1 2004-03-05 devnull switch(r=_alt(a)){
234 76193d7c 2003-09-30 devnull case -1: /* interrupted */
235 76193d7c 2003-09-30 devnull return -1;
236 76193d7c 2003-09-30 devnull case 1: /* nonblocking, didn't accomplish anything */
237 76193d7c 2003-09-30 devnull assert(nb);
238 76193d7c 2003-09-30 devnull return 0;
240 76193d7c 2003-09-30 devnull return 1;
241 76193d7c 2003-09-30 devnull default:
242 76193d7c 2003-09-30 devnull fprint(2, "ERROR: channel alt returned %d\n", r);
243 76193d7c 2003-09-30 devnull abort();
244 76193d7c 2003-09-30 devnull return -1;
249 76193d7c 2003-09-30 devnull recv(Channel *c, void *v)
251 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
252 76193d7c 2003-09-30 devnull return runop(CHANRCV, c, v, 0);
256 76193d7c 2003-09-30 devnull nbrecv(Channel *c, void *v)
258 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
259 76193d7c 2003-09-30 devnull return runop(CHANRCV, c, v, 1);
263 76193d7c 2003-09-30 devnull send(Channel *c, void *v)
265 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
266 76193d7c 2003-09-30 devnull return runop(CHANSND, c, v, 0);
270 76193d7c 2003-09-30 devnull nbsend(Channel *c, void *v)
272 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
273 76193d7c 2003-09-30 devnull return runop(CHANSND, c, v, 1);
276 76193d7c 2003-09-30 devnull static void
277 76193d7c 2003-09-30 devnull channelsize(Channel *c, int sz)
279 76193d7c 2003-09-30 devnull if(c->e != sz){
280 a3785ca2 2004-04-21 devnull fprint(2, "expected channel with elements of size %d, got size %d\n",
281 76193d7c 2003-09-30 devnull sz, c->e);
282 76193d7c 2003-09-30 devnull abort();
287 76193d7c 2003-09-30 devnull sendul(Channel *c, ulong v)
289 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
290 76193d7c 2003-09-30 devnull channelsize(c, sizeof(ulong));
291 76193d7c 2003-09-30 devnull return send(c, &v);
295 76193d7c 2003-09-30 devnull recvul(Channel *c)
297 76193d7c 2003-09-30 devnull ulong v;
299 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
300 76193d7c 2003-09-30 devnull channelsize(c, sizeof(ulong));
301 02a1a5c1 2004-03-05 devnull if(runop(CHANRCV, c, &v, 0) < 0)
302 76193d7c 2003-09-30 devnull return ~0;
303 76193d7c 2003-09-30 devnull return v;
307 76193d7c 2003-09-30 devnull sendp(Channel *c, void *v)
309 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
310 76193d7c 2003-09-30 devnull channelsize(c, sizeof(void*));
311 02a1a5c1 2004-03-05 devnull return runop(CHANSND, c, &v, 0);
315 76193d7c 2003-09-30 devnull recvp(Channel *c)
317 76193d7c 2003-09-30 devnull void *v;
319 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
320 76193d7c 2003-09-30 devnull channelsize(c, sizeof(void*));
321 02a1a5c1 2004-03-05 devnull if(runop(CHANRCV, c, &v, 0) < 0)
322 76193d7c 2003-09-30 devnull return nil;
323 76193d7c 2003-09-30 devnull return v;
327 76193d7c 2003-09-30 devnull nbsendul(Channel *c, ulong v)
329 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
330 76193d7c 2003-09-30 devnull channelsize(c, sizeof(ulong));
331 02a1a5c1 2004-03-05 devnull return runop(CHANSND, c, &v, 1);
335 76193d7c 2003-09-30 devnull nbrecvul(Channel *c)
337 76193d7c 2003-09-30 devnull ulong v;
339 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
340 76193d7c 2003-09-30 devnull channelsize(c, sizeof(ulong));
341 02a1a5c1 2004-03-05 devnull if(runop(CHANRCV, c, &v, 1) == 0)
342 76193d7c 2003-09-30 devnull return 0;
343 76193d7c 2003-09-30 devnull return v;
347 76193d7c 2003-09-30 devnull nbsendp(Channel *c, void *v)
349 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
350 76193d7c 2003-09-30 devnull channelsize(c, sizeof(void*));
351 02a1a5c1 2004-03-05 devnull return runop(CHANSND, c, &v, 1);
355 76193d7c 2003-09-30 devnull nbrecvp(Channel *c)
357 76193d7c 2003-09-30 devnull void *v;
359 02a1a5c1 2004-03-05 devnull setuserpc(getcallerpc(&c));
360 76193d7c 2003-09-30 devnull channelsize(c, sizeof(void*));
361 02a1a5c1 2004-03-05 devnull if(runop(CHANRCV, c, &v, 1) == 0)
362 76193d7c 2003-09-30 devnull return nil;
363 76193d7c 2003-09-30 devnull return v;
366 76193d7c 2003-09-30 devnull static int
367 76193d7c 2003-09-30 devnull emptyentry(Channel *c)
369 76193d7c 2003-09-30 devnull int i, extra;
371 76193d7c 2003-09-30 devnull assert((c->nentry==0 && c->qentry==nil) || (c->nentry && c->qentry));
373 76193d7c 2003-09-30 devnull for(i=0; i<c->nentry; i++)
374 76193d7c 2003-09-30 devnull if(c->qentry[i]==nil)
375 76193d7c 2003-09-30 devnull return i;
377 76193d7c 2003-09-30 devnull extra = 16;
378 76193d7c 2003-09-30 devnull c->nentry += extra;
379 76193d7c 2003-09-30 devnull if(c->nentry > _threadhighnentry) _threadhighnentry = c->nentry;
380 76193d7c 2003-09-30 devnull c->qentry = realloc((void*)c->qentry, c->nentry*sizeof(c->qentry[0]));
381 76193d7c 2003-09-30 devnull if(c->qentry == nil)
382 76193d7c 2003-09-30 devnull sysfatal("realloc channel entries: %r");
383 76193d7c 2003-09-30 devnull _threadmemset(&c->qentry[i], 0, extra*sizeof(c->qentry[0]));
384 76193d7c 2003-09-30 devnull return i;
387 76193d7c 2003-09-30 devnull static void
388 76193d7c 2003-09-30 devnull enqueue(Alt *a, Channel **c)
392 912fba95 2003-11-24 devnull _threaddebug(DBGCHAN, "Queueing alt %p on channel %p", a, a->c);
393 76193d7c 2003-09-30 devnull a->tag = c;
394 76193d7c 2003-09-30 devnull i = emptyentry(a->c);
395 76193d7c 2003-09-30 devnull a->c->qentry[i] = a;
398 76193d7c 2003-09-30 devnull static void
399 76193d7c 2003-09-30 devnull dequeue(Alt *a)
402 76193d7c 2003-09-30 devnull Channel *c;
404 76193d7c 2003-09-30 devnull c = a->c;
405 76193d7c 2003-09-30 devnull for(i=0; i<c->nentry; i++)
406 76193d7c 2003-09-30 devnull if(c->qentry[i]==a){
407 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "Dequeuing alt %p from channel %p", a, a->c);
408 76193d7c 2003-09-30 devnull c->qentry[i] = nil;
409 76193d7c 2003-09-30 devnull if(c->freed)
410 76193d7c 2003-09-30 devnull _chanfree(c);
415 76193d7c 2003-09-30 devnull static void*
416 76193d7c 2003-09-30 devnull altexecbuffered(Alt *a, int willreplace)
418 76193d7c 2003-09-30 devnull uchar *v;
419 76193d7c 2003-09-30 devnull Channel *c;
421 76193d7c 2003-09-30 devnull c = a->c;
422 76193d7c 2003-09-30 devnull /* use buffered channel queue */
423 76193d7c 2003-09-30 devnull if(a->op==CHANRCV && c->n > 0){
424 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "buffer recv alt %p chan %p", a, c);
425 76193d7c 2003-09-30 devnull v = c->v + c->e*(c->f%c->s);
426 76193d7c 2003-09-30 devnull if(!willreplace)
429 76193d7c 2003-09-30 devnull return v;
431 76193d7c 2003-09-30 devnull if(a->op==CHANSND && c->n < c->s){
432 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "buffer send alt %p chan %p", a, c);
433 76193d7c 2003-09-30 devnull v = c->v + c->e*((c->f+c->n)%c->s);
434 76193d7c 2003-09-30 devnull if(!willreplace)
436 76193d7c 2003-09-30 devnull return v;
438 76193d7c 2003-09-30 devnull abort();
439 76193d7c 2003-09-30 devnull return nil;
442 76193d7c 2003-09-30 devnull static void
443 76193d7c 2003-09-30 devnull altcopy(void *dst, void *src, int sz)
445 76193d7c 2003-09-30 devnull if(dst){
447 76193d7c 2003-09-30 devnull memmove(dst, src, sz);
449 76193d7c 2003-09-30 devnull _threadmemset(dst, 0, sz);
453 76193d7c 2003-09-30 devnull static int
454 76193d7c 2003-09-30 devnull altexec(Alt *a, int spl)
456 76193d7c 2003-09-30 devnull volatile Alt *b;
457 76193d7c 2003-09-30 devnull int i, n, otherop;
458 76193d7c 2003-09-30 devnull Channel *c;
459 76193d7c 2003-09-30 devnull void *me, *waiter, *buf;
461 76193d7c 2003-09-30 devnull c = a->c;
463 76193d7c 2003-09-30 devnull /* rendezvous with others */
464 76193d7c 2003-09-30 devnull otherop = (CHANSND+CHANRCV) - a->op;
466 76193d7c 2003-09-30 devnull b = nil;
467 76193d7c 2003-09-30 devnull me = a->v;
468 76193d7c 2003-09-30 devnull for(i=0; i<c->nentry; i++)
469 76193d7c 2003-09-30 devnull if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil)
470 76193d7c 2003-09-30 devnull if(nrand(++n) == 0)
471 76193d7c 2003-09-30 devnull b = c->qentry[i];
472 76193d7c 2003-09-30 devnull if(b != nil){
473 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op==CHANRCV?"recv":"send", a, c, b);
474 76193d7c 2003-09-30 devnull waiter = b->v;
475 76193d7c 2003-09-30 devnull if(c->s && c->n){
477 76193d7c 2003-09-30 devnull * if buffer is full and there are waiters
478 76193d7c 2003-09-30 devnull * and we're meeting a waiter,
479 76193d7c 2003-09-30 devnull * we must be receiving.
481 76193d7c 2003-09-30 devnull * we use the value in the channel buffer,
482 76193d7c 2003-09-30 devnull * copy the waiter's value into the channel buffer
483 76193d7c 2003-09-30 devnull * on behalf of the waiter, and then wake the waiter.
485 76193d7c 2003-09-30 devnull if(a->op!=CHANRCV)
486 76193d7c 2003-09-30 devnull abort();
487 76193d7c 2003-09-30 devnull buf = altexecbuffered(a, 1);
488 76193d7c 2003-09-30 devnull altcopy(me, buf, c->e);
489 76193d7c 2003-09-30 devnull altcopy(buf, waiter, c->e);
491 76193d7c 2003-09-30 devnull if(a->op==CHANRCV)
492 76193d7c 2003-09-30 devnull altcopy(me, waiter, c->e);
494 76193d7c 2003-09-30 devnull altcopy(waiter, me, c->e);
496 76193d7c 2003-09-30 devnull *b->tag = c; /* commits us to rendezvous */
497 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "unlocking the chanlock");
498 76193d7c 2003-09-30 devnull unlock(&chanlock);
499 76193d7c 2003-09-30 devnull _procsplx(spl);
500 76193d7c 2003-09-30 devnull _threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)&chanlock);
501 76193d7c 2003-09-30 devnull while(_threadrendezvous((ulong)b->tag, 0) == ~0)
503 76193d7c 2003-09-30 devnull return 1;
506 76193d7c 2003-09-30 devnull buf = altexecbuffered(a, 0);
507 76193d7c 2003-09-30 devnull if(a->op==CHANRCV)
508 76193d7c 2003-09-30 devnull altcopy(me, buf, c->e);
510 76193d7c 2003-09-30 devnull altcopy(buf, me, c->e);
512 76193d7c 2003-09-30 devnull unlock(&chanlock);
513 76193d7c 2003-09-30 devnull _procsplx(spl);
514 76193d7c 2003-09-30 devnull return 1;