17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <thread.h>
47dd7cddfSDavid du Colombier #include "threadimpl.h"
57dd7cddfSDavid du Colombier
66a5ecc41SDavid du Colombier /* Value to indicate the channel is closed */
76a5ecc41SDavid du Colombier enum {
86a5ecc41SDavid du Colombier CHANCLOSD = 0xc105ed,
96a5ecc41SDavid du Colombier };
106a5ecc41SDavid du Colombier
116a5ecc41SDavid du Colombier static char errcl[] = "channel was closed";
129a747e4fSDavid du Colombier static Lock chanlock; /* central channel access lock */
139a747e4fSDavid du Colombier
149a747e4fSDavid du Colombier static void enqueue(Alt*, Channel**);
159a747e4fSDavid du Colombier static void dequeue(Alt*);
169a747e4fSDavid du Colombier static int canexec(Alt*);
179a747e4fSDavid du Colombier static int altexec(Alt*, int);
187dd7cddfSDavid du Colombier
196a5ecc41SDavid du Colombier #define Closed ((void*)CHANCLOSD)
206a5ecc41SDavid du Colombier #define Intred ((void*)~0) /* interrupted */
216a5ecc41SDavid du Colombier
226b6b9ac8SDavid du Colombier static void
_chanfree(Channel * c)236b6b9ac8SDavid du Colombier _chanfree(Channel *c)
249a747e4fSDavid du Colombier {
259a747e4fSDavid du Colombier int i, inuse;
267dd7cddfSDavid du Colombier
276a5ecc41SDavid du Colombier if(c->closed == 1) /* chanclose is ongoing */
286a5ecc41SDavid du Colombier inuse = 1;
296a5ecc41SDavid du Colombier else{
309a747e4fSDavid du Colombier inuse = 0;
316a5ecc41SDavid du Colombier for(i = 0; i < c->nentry; i++) /* alt ongoing */
329a747e4fSDavid du Colombier if(c->qentry[i])
339a747e4fSDavid du Colombier inuse = 1;
346a5ecc41SDavid du Colombier }
359a747e4fSDavid du Colombier if(inuse)
367dd7cddfSDavid du Colombier c->freed = 1;
379a747e4fSDavid du Colombier else{
389a747e4fSDavid du Colombier if(c->qentry)
396b6b9ac8SDavid du Colombier free(c->qentry);
409a747e4fSDavid du Colombier free(c);
417dd7cddfSDavid du Colombier }
426b6b9ac8SDavid du Colombier }
436b6b9ac8SDavid du Colombier
446b6b9ac8SDavid du Colombier void
chanfree(Channel * c)456b6b9ac8SDavid du Colombier chanfree(Channel *c)
466b6b9ac8SDavid du Colombier {
476b6b9ac8SDavid du Colombier lock(&chanlock);
486b6b9ac8SDavid du Colombier _chanfree(c);
497dd7cddfSDavid du Colombier unlock(&chanlock);
507dd7cddfSDavid du Colombier }
517dd7cddfSDavid du Colombier
5259cc4ca5SDavid du Colombier int
chaninit(Channel * c,int elemsize,int elemcnt)539a747e4fSDavid du Colombier chaninit(Channel *c, int elemsize, int elemcnt)
549a747e4fSDavid du Colombier {
5559cc4ca5SDavid du Colombier if(elemcnt < 0 || elemsize <= 0 || c == nil)
5659cc4ca5SDavid du Colombier return -1;
5759cc4ca5SDavid du Colombier c->f = 0;
5859cc4ca5SDavid du Colombier c->n = 0;
596a5ecc41SDavid du Colombier c->closed = 0;
6059cc4ca5SDavid du Colombier c->freed = 0;
6159cc4ca5SDavid du Colombier c->e = elemsize;
629a747e4fSDavid du Colombier c->s = elemcnt;
639a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "chaninit %p", c);
6459cc4ca5SDavid du Colombier return 1;
6559cc4ca5SDavid du Colombier }
6659cc4ca5SDavid du Colombier
677dd7cddfSDavid du Colombier Channel*
chancreate(int elemsize,int elemcnt)689a747e4fSDavid du Colombier chancreate(int elemsize, int elemcnt)
699a747e4fSDavid du Colombier {
707dd7cddfSDavid du Colombier Channel *c;
717dd7cddfSDavid du Colombier
727dd7cddfSDavid du Colombier if(elemcnt < 0 || elemsize <= 0)
737dd7cddfSDavid du Colombier return nil;
749a747e4fSDavid du Colombier c = _threadmalloc(sizeof(Channel)+elemsize*elemcnt, 1);
757dd7cddfSDavid du Colombier c->e = elemsize;
769a747e4fSDavid du Colombier c->s = elemcnt;
779a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "chancreate %p", c);
787dd7cddfSDavid du Colombier return c;
797dd7cddfSDavid du Colombier }
807dd7cddfSDavid du Colombier
816a5ecc41SDavid du Colombier static int
isopenfor(Channel * c,int op)826a5ecc41SDavid du Colombier isopenfor(Channel *c, int op)
836a5ecc41SDavid du Colombier {
846a5ecc41SDavid du Colombier return c->closed == 0 || (op == CHANRCV && c->n > 0);
856a5ecc41SDavid du Colombier }
866a5ecc41SDavid du Colombier
877dd7cddfSDavid du Colombier int
alt(Alt * alts)889a747e4fSDavid du Colombier alt(Alt *alts)
899a747e4fSDavid du Colombier {
906a5ecc41SDavid du Colombier Alt *a, *xa, *ca;
916b6b9ac8SDavid du Colombier Channel volatile *c;
926a5ecc41SDavid du Colombier int n, s, waiting, allreadycl;
9374f16c81SDavid du Colombier void* r;
947dd7cddfSDavid du Colombier Thread *t;
957dd7cddfSDavid du Colombier
969a747e4fSDavid du Colombier /*
979a747e4fSDavid du Colombier * The point of going splhi here is that note handlers
989a747e4fSDavid du Colombier * might reasonably want to use channel operations,
999a747e4fSDavid du Colombier * but that will hang if the note comes while we hold the
1009a747e4fSDavid du Colombier * chanlock. Instead, we delay the note until we've dropped
1019a747e4fSDavid du Colombier * the lock.
1029a747e4fSDavid du Colombier */
1039a747e4fSDavid du Colombier t = _threadgetproc()->thread;
1049a747e4fSDavid du Colombier if(t->moribund || _threadexitsallstatus)
1059a747e4fSDavid du Colombier yield(); /* won't return */
1069a747e4fSDavid du Colombier s = _procsplhi();
1077dd7cddfSDavid du Colombier lock(&chanlock);
1089a747e4fSDavid du Colombier t->alt = alts;
1099a747e4fSDavid du Colombier t->chan = Chanalt;
1107dd7cddfSDavid du Colombier
1119a747e4fSDavid du Colombier /* test whether any channels can proceed */
1129a747e4fSDavid du Colombier n = 0;
1137dd7cddfSDavid du Colombier a = nil;
1149a747e4fSDavid du Colombier
1159a747e4fSDavid du Colombier for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){
1169a747e4fSDavid du Colombier xa->entryno = -1;
1179a747e4fSDavid du Colombier if(xa->op == CHANNOP)
1189a747e4fSDavid du Colombier continue;
1197dd7cddfSDavid du Colombier
1207dd7cddfSDavid du Colombier c = xa->c;
1216b6b9ac8SDavid du Colombier if(c==nil){
1226b6b9ac8SDavid du Colombier unlock(&chanlock);
1236b6b9ac8SDavid du Colombier _procsplx(s);
1246b6b9ac8SDavid du Colombier t->chan = Channone;
1256b6b9ac8SDavid du Colombier return -1;
1266b6b9ac8SDavid du Colombier }
1276b6b9ac8SDavid du Colombier
1286a5ecc41SDavid du Colombier if(isopenfor(c, xa->op) && canexec(xa))
1299a747e4fSDavid du Colombier if(nrand(++n) == 0)
1307dd7cddfSDavid du Colombier a = xa;
1317dd7cddfSDavid du Colombier }
1327dd7cddfSDavid du Colombier
1336a5ecc41SDavid du Colombier
1347dd7cddfSDavid du Colombier if(a==nil){
1359a747e4fSDavid du Colombier /* nothing can proceed */
1369a747e4fSDavid du Colombier if(xa->op == CHANNOBLK){
1379a747e4fSDavid du Colombier unlock(&chanlock);
1389a747e4fSDavid du Colombier _procsplx(s);
1399a747e4fSDavid du Colombier t->chan = Channone;
1406a5ecc41SDavid du Colombier if(xa->op == CHANNOBLK)
1419a747e4fSDavid du Colombier return xa - alts;
1429a747e4fSDavid du Colombier }
1439a747e4fSDavid du Colombier
1446a5ecc41SDavid du Colombier /* enqueue on all channels open for us. */
1457dd7cddfSDavid du Colombier c = nil;
1466a5ecc41SDavid du Colombier ca = nil;
1476a5ecc41SDavid du Colombier waiting = 0;
1486a5ecc41SDavid du Colombier allreadycl = 0;
1496a5ecc41SDavid du Colombier for(xa=alts; xa->op!=CHANEND; xa++)
1509a747e4fSDavid du Colombier if(xa->op==CHANNOP)
1519a747e4fSDavid du Colombier continue;
1526a5ecc41SDavid du Colombier else if(isopenfor(xa->c, xa->op)){
1536a5ecc41SDavid du Colombier waiting = 1;
1549a747e4fSDavid du Colombier enqueue(xa, &c);
1556a5ecc41SDavid du Colombier } else if(xa->err != errcl)
1566a5ecc41SDavid du Colombier ca = xa;
1576a5ecc41SDavid du Colombier else
1586a5ecc41SDavid du Colombier allreadycl = 1;
1597dd7cddfSDavid du Colombier
1606a5ecc41SDavid du Colombier if(waiting == 0)
1616a5ecc41SDavid du Colombier if(ca != nil){
1626a5ecc41SDavid du Colombier /* everything was closed, select last channel */
1636a5ecc41SDavid du Colombier ca->err = errcl;
1646a5ecc41SDavid du Colombier unlock(&chanlock);
1656a5ecc41SDavid du Colombier _procsplx(s);
1666a5ecc41SDavid du Colombier t->chan = Channone;
1676a5ecc41SDavid du Colombier return ca - alts;
1686a5ecc41SDavid du Colombier } else if(allreadycl){
1696a5ecc41SDavid du Colombier /* everything was already closed */
1706a5ecc41SDavid du Colombier unlock(&chanlock);
1716a5ecc41SDavid du Colombier _procsplx(s);
1726a5ecc41SDavid du Colombier t->chan = Channone;
1736a5ecc41SDavid du Colombier return -1;
1746a5ecc41SDavid du Colombier }
1759a747e4fSDavid du Colombier /*
1769a747e4fSDavid du Colombier * wait for successful rendezvous.
1779a747e4fSDavid du Colombier * we can't just give up if the rendezvous
1789a747e4fSDavid du Colombier * is interrupted -- someone else might come
1799a747e4fSDavid du Colombier * along and try to rendezvous with us, so
1809a747e4fSDavid du Colombier * we need to be here.
1816a5ecc41SDavid du Colombier * if the channel was closed, the op is done
1826a5ecc41SDavid du Colombier * and we flag an error for the entry.
1837dd7cddfSDavid du Colombier */
1849a747e4fSDavid du Colombier Again:
1859a747e4fSDavid du Colombier unlock(&chanlock);
1869a747e4fSDavid du Colombier _procsplx(s);
18774f16c81SDavid du Colombier r = _threadrendezvous(&c, 0);
1889a747e4fSDavid du Colombier s = _procsplhi();
1899a747e4fSDavid du Colombier lock(&chanlock);
1909a747e4fSDavid du Colombier
1916a5ecc41SDavid du Colombier if(r==Intred){ /* interrupted */
1929a747e4fSDavid du Colombier if(c!=nil) /* someone will meet us; go back */
1939a747e4fSDavid du Colombier goto Again;
1949a747e4fSDavid du Colombier c = (Channel*)~0; /* so no one tries to meet us */
1959a747e4fSDavid du Colombier }
1969a747e4fSDavid du Colombier
1979a747e4fSDavid du Colombier /* dequeue from channels, find selected one */
1987dd7cddfSDavid du Colombier a = nil;
1999a747e4fSDavid du Colombier for(xa=alts; xa->op!=CHANEND; xa++){
2009a747e4fSDavid du Colombier if(xa->op==CHANNOP)
2019a747e4fSDavid du Colombier continue;
2026a5ecc41SDavid du Colombier if(xa->c == c){
2037dd7cddfSDavid du Colombier a = xa;
2046a5ecc41SDavid du Colombier a->err = nil;
2056a5ecc41SDavid du Colombier if(r == Closed)
2066a5ecc41SDavid du Colombier a->err = errcl;
2076a5ecc41SDavid du Colombier }
2089a747e4fSDavid du Colombier dequeue(xa);
2097dd7cddfSDavid du Colombier }
2107dd7cddfSDavid du Colombier unlock(&chanlock);
2119a747e4fSDavid du Colombier _procsplx(s);
2129a747e4fSDavid du Colombier if(a == nil){ /* we were interrupted */
2139a747e4fSDavid du Colombier assert(c==(Channel*)~0);
2147dd7cddfSDavid du Colombier return -1;
2157dd7cddfSDavid du Colombier }
2166a5ecc41SDavid du Colombier }else
2179a747e4fSDavid du Colombier altexec(a, s); /* unlocks chanlock, does splx */
2189a747e4fSDavid du Colombier _sched();
2199a747e4fSDavid du Colombier t->chan = Channone;
2207dd7cddfSDavid du Colombier return a - alts;
2217dd7cddfSDavid du Colombier }
2227dd7cddfSDavid du Colombier
2236a5ecc41SDavid du Colombier int
chanclose(Channel * c)2246a5ecc41SDavid du Colombier chanclose(Channel *c)
2256a5ecc41SDavid du Colombier {
2266a5ecc41SDavid du Colombier Alt *a;
227*b39189fdSDavid du Colombier int i, s;
2286a5ecc41SDavid du Colombier
2296a5ecc41SDavid du Colombier s = _procsplhi(); /* note handlers; see :/^alt */
2306a5ecc41SDavid du Colombier lock(&chanlock);
2316a5ecc41SDavid du Colombier if(c->closed){
2326a5ecc41SDavid du Colombier /* Already close; we fail but it's ok. don't print */
2336a5ecc41SDavid du Colombier unlock(&chanlock);
2346a5ecc41SDavid du Colombier _procsplx(s);
2356a5ecc41SDavid du Colombier return -1;
2366a5ecc41SDavid du Colombier }
2376a5ecc41SDavid du Colombier c->closed = 1; /* Being closed */
2386a5ecc41SDavid du Colombier /*
239*b39189fdSDavid du Colombier * Locate entries that will fail due to close
2406a5ecc41SDavid du Colombier * (send, and receive if nothing buffered) and wake them up.
241*b39189fdSDavid du Colombier * the situation cannot change because all queries
242*b39189fdSDavid du Colombier * should be committed by now and new ones will find the channel
243*b39189fdSDavid du Colombier * closed. We still need to take the lock during the iteration
244*b39189fdSDavid du Colombier * because we can wake threads on qentrys we have not seen yet
245*b39189fdSDavid du Colombier * as in alt and there would be a race in the access to *a.
2466a5ecc41SDavid du Colombier */
2476a5ecc41SDavid du Colombier for(i = 0; i < c->nentry; i++){
2486a5ecc41SDavid du Colombier if((a = c->qentry[i]) == nil || *a->tag != nil)
2496a5ecc41SDavid du Colombier continue;
250*b39189fdSDavid du Colombier
2516a5ecc41SDavid du Colombier if(a->op != CHANSND && (a->op != CHANRCV || c->n != 0))
2526a5ecc41SDavid du Colombier continue;
2536a5ecc41SDavid du Colombier *a->tag = c;
2546a5ecc41SDavid du Colombier unlock(&chanlock);
2556a5ecc41SDavid du Colombier _procsplx(s);
2566a5ecc41SDavid du Colombier while(_threadrendezvous(a->tag, Closed) == Intred)
2576a5ecc41SDavid du Colombier ;
2586a5ecc41SDavid du Colombier s = _procsplhi();
2596a5ecc41SDavid du Colombier lock(&chanlock);
2606a5ecc41SDavid du Colombier }
2616a5ecc41SDavid du Colombier
2626a5ecc41SDavid du Colombier c->closed = 2; /* Fully closed */
2636a5ecc41SDavid du Colombier if(c->freed)
2646a5ecc41SDavid du Colombier _chanfree(c);
2656a5ecc41SDavid du Colombier unlock(&chanlock);
2666a5ecc41SDavid du Colombier _procsplx(s);
2676a5ecc41SDavid du Colombier return 0;
2686a5ecc41SDavid du Colombier }
2696a5ecc41SDavid du Colombier
2706a5ecc41SDavid du Colombier int
chanclosing(Channel * c)271*b39189fdSDavid du Colombier chanclosing(Channel *c)
272*b39189fdSDavid du Colombier {
273*b39189fdSDavid du Colombier int n, s;
274*b39189fdSDavid du Colombier
275*b39189fdSDavid du Colombier s = _procsplhi(); /* note handlers; see :/^alt */
276*b39189fdSDavid du Colombier lock(&chanlock);
277*b39189fdSDavid du Colombier if(c->closed == 0)
278*b39189fdSDavid du Colombier n = -1;
279*b39189fdSDavid du Colombier else
280*b39189fdSDavid du Colombier n = c->n;
281*b39189fdSDavid du Colombier unlock(&chanlock);
282*b39189fdSDavid du Colombier _procsplx(s);
283*b39189fdSDavid du Colombier return n;
284*b39189fdSDavid du Colombier }
285*b39189fdSDavid du Colombier
286*b39189fdSDavid du Colombier /*
287*b39189fdSDavid du Colombier * superseded by chanclosing
288*b39189fdSDavid du Colombier int
2896a5ecc41SDavid du Colombier chanisclosed(Channel *c)
2906a5ecc41SDavid du Colombier {
291*b39189fdSDavid du Colombier return chanisclosing(c) >= 0;
2926a5ecc41SDavid du Colombier }
293*b39189fdSDavid du Colombier */
2946a5ecc41SDavid du Colombier
2959a747e4fSDavid du Colombier static int
runop(int op,Channel * c,void * v,int nb)2969a747e4fSDavid du Colombier runop(int op, Channel *c, void *v, int nb)
2979a747e4fSDavid du Colombier {
2989a747e4fSDavid du Colombier int r;
2999a747e4fSDavid du Colombier Alt a[2];
3007dd7cddfSDavid du Colombier
3019a747e4fSDavid du Colombier /*
3029a747e4fSDavid du Colombier * we could do this without calling alt,
3039a747e4fSDavid du Colombier * but the only reason would be performance,
3049a747e4fSDavid du Colombier * and i'm not convinced it matters.
3059a747e4fSDavid du Colombier */
3069a747e4fSDavid du Colombier a[0].op = op;
3079a747e4fSDavid du Colombier a[0].c = c;
3089a747e4fSDavid du Colombier a[0].v = v;
3096a5ecc41SDavid du Colombier a[0].err = nil;
3109a747e4fSDavid du Colombier a[1].op = CHANEND;
3119a747e4fSDavid du Colombier if(nb)
3129a747e4fSDavid du Colombier a[1].op = CHANNOBLK;
3139a747e4fSDavid du Colombier switch(r=alt(a)){
3149a747e4fSDavid du Colombier case -1: /* interrupted */
3157dd7cddfSDavid du Colombier return -1;
3169a747e4fSDavid du Colombier case 1: /* nonblocking, didn't accomplish anything */
3179a747e4fSDavid du Colombier assert(nb);
3187dd7cddfSDavid du Colombier return 0;
3199a747e4fSDavid du Colombier case 0:
3206a5ecc41SDavid du Colombier /*
3216a5ecc41SDavid du Colombier * Okay, but return -1 if the op is done because of a close.
3226a5ecc41SDavid du Colombier */
3236a5ecc41SDavid du Colombier if(a[0].err != nil)
3246a5ecc41SDavid du Colombier return -1;
3259a747e4fSDavid du Colombier return 1;
3269a747e4fSDavid du Colombier default:
3279a747e4fSDavid du Colombier fprint(2, "ERROR: channel alt returned %d\n", r);
3289a747e4fSDavid du Colombier abort();
3299a747e4fSDavid du Colombier return -1;
3309a747e4fSDavid du Colombier }
3317dd7cddfSDavid du Colombier }
3327dd7cddfSDavid du Colombier
3337dd7cddfSDavid du Colombier int
recv(Channel * c,void * v)3349a747e4fSDavid du Colombier recv(Channel *c, void *v)
3359a747e4fSDavid du Colombier {
3369a747e4fSDavid du Colombier return runop(CHANRCV, c, v, 0);
3377dd7cddfSDavid du Colombier }
3387dd7cddfSDavid du Colombier
3397dd7cddfSDavid du Colombier int
nbrecv(Channel * c,void * v)3409a747e4fSDavid du Colombier nbrecv(Channel *c, void *v)
3419a747e4fSDavid du Colombier {
3429a747e4fSDavid du Colombier return runop(CHANRCV, c, v, 1);
3437dd7cddfSDavid du Colombier }
3447dd7cddfSDavid du Colombier
3457dd7cddfSDavid du Colombier int
send(Channel * c,void * v)3469a747e4fSDavid du Colombier send(Channel *c, void *v)
3479a747e4fSDavid du Colombier {
3489a747e4fSDavid du Colombier return runop(CHANSND, c, v, 0);
3497dd7cddfSDavid du Colombier }
3507dd7cddfSDavid du Colombier
3517dd7cddfSDavid du Colombier int
nbsend(Channel * c,void * v)3529a747e4fSDavid du Colombier nbsend(Channel *c, void *v)
3539a747e4fSDavid du Colombier {
3549a747e4fSDavid du Colombier return runop(CHANSND, c, v, 1);
3559a747e4fSDavid du Colombier }
3569a747e4fSDavid du Colombier
3579a747e4fSDavid du Colombier static void
channelsize(Channel * c,int sz)3589a747e4fSDavid du Colombier channelsize(Channel *c, int sz)
3599a747e4fSDavid du Colombier {
3609a747e4fSDavid du Colombier if(c->e != sz){
36114cc0f53SDavid du Colombier fprint(2, "expected channel with elements of size %d, got size %d\n",
3629a747e4fSDavid du Colombier sz, c->e);
3639a747e4fSDavid du Colombier abort();
3649a747e4fSDavid du Colombier }
3659a747e4fSDavid du Colombier }
3669a747e4fSDavid du Colombier
3679a747e4fSDavid du Colombier int
sendul(Channel * c,ulong v)3689a747e4fSDavid du Colombier sendul(Channel *c, ulong v)
3699a747e4fSDavid du Colombier {
3709a747e4fSDavid du Colombier channelsize(c, sizeof(ulong));
3717dd7cddfSDavid du Colombier return send(c, &v);
3727dd7cddfSDavid du Colombier }
3737dd7cddfSDavid du Colombier
3747dd7cddfSDavid du Colombier ulong
recvul(Channel * c)3759a747e4fSDavid du Colombier recvul(Channel *c)
3769a747e4fSDavid du Colombier {
3777dd7cddfSDavid du Colombier ulong v;
3787dd7cddfSDavid du Colombier
3799a747e4fSDavid du Colombier channelsize(c, sizeof(ulong));
3809a747e4fSDavid du Colombier if(recv(c, &v) < 0)
3819a747e4fSDavid du Colombier return ~0;
3827dd7cddfSDavid du Colombier return v;
3837dd7cddfSDavid du Colombier }
3847dd7cddfSDavid du Colombier
3857dd7cddfSDavid du Colombier int
sendp(Channel * c,void * v)3869a747e4fSDavid du Colombier sendp(Channel *c, void *v)
3879a747e4fSDavid du Colombier {
3889a747e4fSDavid du Colombier channelsize(c, sizeof(void*));
3897dd7cddfSDavid du Colombier return send(c, &v);
3907dd7cddfSDavid du Colombier }
3917dd7cddfSDavid du Colombier
3927dd7cddfSDavid du Colombier void*
recvp(Channel * c)3939a747e4fSDavid du Colombier recvp(Channel *c)
3949a747e4fSDavid du Colombier {
3957dd7cddfSDavid du Colombier void *v;
3967dd7cddfSDavid du Colombier
3979a747e4fSDavid du Colombier channelsize(c, sizeof(void*));
3989a747e4fSDavid du Colombier if(recv(c, &v) < 0)
3999a747e4fSDavid du Colombier return nil;
4007dd7cddfSDavid du Colombier return v;
4017dd7cddfSDavid du Colombier }
4027dd7cddfSDavid du Colombier
4037dd7cddfSDavid du Colombier int
nbsendul(Channel * c,ulong v)4049a747e4fSDavid du Colombier nbsendul(Channel *c, ulong v)
4059a747e4fSDavid du Colombier {
4069a747e4fSDavid du Colombier channelsize(c, sizeof(ulong));
4077dd7cddfSDavid du Colombier return nbsend(c, &v);
4087dd7cddfSDavid du Colombier }
4097dd7cddfSDavid du Colombier
4107dd7cddfSDavid du Colombier ulong
nbrecvul(Channel * c)4119a747e4fSDavid du Colombier nbrecvul(Channel *c)
4129a747e4fSDavid du Colombier {
4137dd7cddfSDavid du Colombier ulong v;
4147dd7cddfSDavid du Colombier
4159a747e4fSDavid du Colombier channelsize(c, sizeof(ulong));
4167dd7cddfSDavid du Colombier if(nbrecv(c, &v) == 0)
4177dd7cddfSDavid du Colombier return 0;
4187dd7cddfSDavid du Colombier return v;
4197dd7cddfSDavid du Colombier }
4207dd7cddfSDavid du Colombier
4217dd7cddfSDavid du Colombier int
nbsendp(Channel * c,void * v)4229a747e4fSDavid du Colombier nbsendp(Channel *c, void *v)
4239a747e4fSDavid du Colombier {
4249a747e4fSDavid du Colombier channelsize(c, sizeof(void*));
4257dd7cddfSDavid du Colombier return nbsend(c, &v);
4267dd7cddfSDavid du Colombier }
4277dd7cddfSDavid du Colombier
4287dd7cddfSDavid du Colombier void*
nbrecvp(Channel * c)4299a747e4fSDavid du Colombier nbrecvp(Channel *c)
4309a747e4fSDavid du Colombier {
4317dd7cddfSDavid du Colombier void *v;
4327dd7cddfSDavid du Colombier
4339a747e4fSDavid du Colombier channelsize(c, sizeof(void*));
4347dd7cddfSDavid du Colombier if(nbrecv(c, &v) == 0)
4357dd7cddfSDavid du Colombier return nil;
4367dd7cddfSDavid du Colombier return v;
4377dd7cddfSDavid du Colombier }
4389a747e4fSDavid du Colombier
4399a747e4fSDavid du Colombier static int
emptyentry(Channel * c)4409a747e4fSDavid du Colombier emptyentry(Channel *c)
4419a747e4fSDavid du Colombier {
4429a747e4fSDavid du Colombier int i, extra;
4439a747e4fSDavid du Colombier
4449a747e4fSDavid du Colombier assert((c->nentry==0 && c->qentry==nil) || (c->nentry && c->qentry));
4459a747e4fSDavid du Colombier
4469a747e4fSDavid du Colombier for(i=0; i<c->nentry; i++)
4479a747e4fSDavid du Colombier if(c->qentry[i]==nil)
4489a747e4fSDavid du Colombier return i;
4499a747e4fSDavid du Colombier
4509a747e4fSDavid du Colombier extra = 16;
4519a747e4fSDavid du Colombier c->nentry += extra;
4526b6b9ac8SDavid du Colombier c->qentry = realloc((void*)c->qentry, c->nentry*sizeof(c->qentry[0]));
4539a747e4fSDavid du Colombier if(c->qentry == nil)
4549a747e4fSDavid du Colombier sysfatal("realloc channel entries: %r");
4556b6b9ac8SDavid du Colombier memset(&c->qentry[i], 0, extra*sizeof(c->qentry[0]));
4569a747e4fSDavid du Colombier return i;
4579a747e4fSDavid du Colombier }
4589a747e4fSDavid du Colombier
4599a747e4fSDavid du Colombier static void
enqueue(Alt * a,Channel ** c)4609a747e4fSDavid du Colombier enqueue(Alt *a, Channel **c)
4619a747e4fSDavid du Colombier {
4629a747e4fSDavid du Colombier int i;
4639a747e4fSDavid du Colombier
4649a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "Queuing alt %p on channel %p", a, a->c);
4659a747e4fSDavid du Colombier a->tag = c;
4669a747e4fSDavid du Colombier i = emptyentry(a->c);
4679a747e4fSDavid du Colombier a->c->qentry[i] = a;
4689a747e4fSDavid du Colombier }
4699a747e4fSDavid du Colombier
4709a747e4fSDavid du Colombier static void
dequeue(Alt * a)4719a747e4fSDavid du Colombier dequeue(Alt *a)
4729a747e4fSDavid du Colombier {
4739a747e4fSDavid du Colombier int i;
4749a747e4fSDavid du Colombier Channel *c;
4759a747e4fSDavid du Colombier
4769a747e4fSDavid du Colombier c = a->c;
4779a747e4fSDavid du Colombier for(i=0; i<c->nentry; i++)
4789a747e4fSDavid du Colombier if(c->qentry[i]==a){
4799a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "Dequeuing alt %p from channel %p", a, a->c);
4809a747e4fSDavid du Colombier c->qentry[i] = nil;
4816a5ecc41SDavid du Colombier /* release if freed and not closing */
4826a5ecc41SDavid du Colombier if(c->freed && c->closed != 1)
4836b6b9ac8SDavid du Colombier _chanfree(c);
4849a747e4fSDavid du Colombier return;
4859a747e4fSDavid du Colombier }
4869a747e4fSDavid du Colombier }
4879a747e4fSDavid du Colombier
4889a747e4fSDavid du Colombier static int
canexec(Alt * a)4899a747e4fSDavid du Colombier canexec(Alt *a)
4909a747e4fSDavid du Colombier {
4919a747e4fSDavid du Colombier int i, otherop;
4929a747e4fSDavid du Colombier Channel *c;
4939a747e4fSDavid du Colombier
4949a747e4fSDavid du Colombier c = a->c;
4959a747e4fSDavid du Colombier /* are there senders or receivers blocked? */
4969a747e4fSDavid du Colombier otherop = (CHANSND+CHANRCV) - a->op;
4979a747e4fSDavid du Colombier for(i=0; i<c->nentry; i++)
4989a747e4fSDavid du Colombier if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil){
4999a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, c);
5009a747e4fSDavid du Colombier return 1;
5019a747e4fSDavid du Colombier }
5029a747e4fSDavid du Colombier
5039a747e4fSDavid du Colombier /* is there room in the channel? */
5049a747e4fSDavid du Colombier if((a->op==CHANSND && c->n < c->s)
5059a747e4fSDavid du Colombier || (a->op==CHANRCV && c->n > 0)){
5069a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "can buffer alt %p chan %p", a, c);
5079a747e4fSDavid du Colombier return 1;
5089a747e4fSDavid du Colombier }
5099a747e4fSDavid du Colombier
5109a747e4fSDavid du Colombier return 0;
5119a747e4fSDavid du Colombier }
5129a747e4fSDavid du Colombier
5139a747e4fSDavid du Colombier static void*
altexecbuffered(Alt * a,int willreplace)5149a747e4fSDavid du Colombier altexecbuffered(Alt *a, int willreplace)
5159a747e4fSDavid du Colombier {
5169a747e4fSDavid du Colombier uchar *v;
5179a747e4fSDavid du Colombier Channel *c;
5189a747e4fSDavid du Colombier
5199a747e4fSDavid du Colombier c = a->c;
5209a747e4fSDavid du Colombier /* use buffered channel queue */
5219a747e4fSDavid du Colombier if(a->op==CHANRCV && c->n > 0){
5229a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "buffer recv alt %p chan %p", a, c);
5239a747e4fSDavid du Colombier v = c->v + c->e*(c->f%c->s);
5249a747e4fSDavid du Colombier if(!willreplace)
5259a747e4fSDavid du Colombier c->n--;
5269a747e4fSDavid du Colombier c->f++;
5279a747e4fSDavid du Colombier return v;
5289a747e4fSDavid du Colombier }
5299a747e4fSDavid du Colombier if(a->op==CHANSND && c->n < c->s){
5309a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "buffer send alt %p chan %p", a, c);
5319a747e4fSDavid du Colombier v = c->v + c->e*((c->f+c->n)%c->s);
5329a747e4fSDavid du Colombier if(!willreplace)
5339a747e4fSDavid du Colombier c->n++;
5349a747e4fSDavid du Colombier return v;
5359a747e4fSDavid du Colombier }
5369a747e4fSDavid du Colombier abort();
5379a747e4fSDavid du Colombier return nil;
5389a747e4fSDavid du Colombier }
5399a747e4fSDavid du Colombier
5409a747e4fSDavid du Colombier static void
altcopy(void * dst,void * src,int sz)5419a747e4fSDavid du Colombier altcopy(void *dst, void *src, int sz)
5429a747e4fSDavid du Colombier {
5439a747e4fSDavid du Colombier if(dst){
5449a747e4fSDavid du Colombier if(src)
5459a747e4fSDavid du Colombier memmove(dst, src, sz);
5469a747e4fSDavid du Colombier else
5479a747e4fSDavid du Colombier memset(dst, 0, sz);
5489a747e4fSDavid du Colombier }
5499a747e4fSDavid du Colombier }
5509a747e4fSDavid du Colombier
5519a747e4fSDavid du Colombier static int
altexec(Alt * a,int spl)5529a747e4fSDavid du Colombier altexec(Alt *a, int spl)
5539a747e4fSDavid du Colombier {
5549a747e4fSDavid du Colombier volatile Alt *b;
5559a747e4fSDavid du Colombier int i, n, otherop;
5569a747e4fSDavid du Colombier Channel *c;
5579a747e4fSDavid du Colombier void *me, *waiter, *buf;
5589a747e4fSDavid du Colombier
5599a747e4fSDavid du Colombier c = a->c;
5609a747e4fSDavid du Colombier
5619a747e4fSDavid du Colombier /* rendezvous with others */
5629a747e4fSDavid du Colombier otherop = (CHANSND+CHANRCV) - a->op;
5639a747e4fSDavid du Colombier n = 0;
5649a747e4fSDavid du Colombier b = nil;
5659a747e4fSDavid du Colombier me = a->v;
5669a747e4fSDavid du Colombier for(i=0; i<c->nentry; i++)
5679a747e4fSDavid du Colombier if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil)
5689a747e4fSDavid du Colombier if(nrand(++n) == 0)
5699a747e4fSDavid du Colombier b = c->qentry[i];
5709a747e4fSDavid du Colombier if(b != nil){
5719a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op==CHANRCV?"recv":"send", a, c, b);
5729a747e4fSDavid du Colombier waiter = b->v;
5739a747e4fSDavid du Colombier if(c->s && c->n){
5749a747e4fSDavid du Colombier /*
5759a747e4fSDavid du Colombier * if buffer is full and there are waiters
5769a747e4fSDavid du Colombier * and we're meeting a waiter,
5779a747e4fSDavid du Colombier * we must be receiving.
5789a747e4fSDavid du Colombier *
5799a747e4fSDavid du Colombier * we use the value in the channel buffer,
5809a747e4fSDavid du Colombier * copy the waiter's value into the channel buffer
5819a747e4fSDavid du Colombier * on behalf of the waiter, and then wake the waiter.
5829a747e4fSDavid du Colombier */
5839a747e4fSDavid du Colombier if(a->op!=CHANRCV)
5849a747e4fSDavid du Colombier abort();
5859a747e4fSDavid du Colombier buf = altexecbuffered(a, 1);
5869a747e4fSDavid du Colombier altcopy(me, buf, c->e);
5879a747e4fSDavid du Colombier altcopy(buf, waiter, c->e);
5889a747e4fSDavid du Colombier }else{
5899a747e4fSDavid du Colombier if(a->op==CHANRCV)
5909a747e4fSDavid du Colombier altcopy(me, waiter, c->e);
5919a747e4fSDavid du Colombier else
5929a747e4fSDavid du Colombier altcopy(waiter, me, c->e);
5939a747e4fSDavid du Colombier }
5949a747e4fSDavid du Colombier *b->tag = c; /* commits us to rendezvous */
5959a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "unlocking the chanlock");
5969a747e4fSDavid du Colombier unlock(&chanlock);
5979a747e4fSDavid du Colombier _procsplx(spl);
5989a747e4fSDavid du Colombier _threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)&chanlock);
5996a5ecc41SDavid du Colombier while(_threadrendezvous(b->tag, 0) == Intred)
6009a747e4fSDavid du Colombier ;
6019a747e4fSDavid du Colombier return 1;
6029a747e4fSDavid du Colombier }
6039a747e4fSDavid du Colombier
6049a747e4fSDavid du Colombier buf = altexecbuffered(a, 0);
6059a747e4fSDavid du Colombier if(a->op==CHANRCV)
6069a747e4fSDavid du Colombier altcopy(me, buf, c->e);
6079a747e4fSDavid du Colombier else
6089a747e4fSDavid du Colombier altcopy(buf, me, c->e);
6099a747e4fSDavid du Colombier
6109a747e4fSDavid du Colombier unlock(&chanlock);
6119a747e4fSDavid du Colombier _procsplx(spl);
6129a747e4fSDavid du Colombier return 1;
6139a747e4fSDavid du Colombier }
614