xref: /plan9/sys/src/libthread/channel.c (revision b39189fd423aed869c5cf5189bc504918cff969b)
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