ref: dd16eef8365cb0b9e4805c23291aadcaa596dd87
dir: /libinterp/alt.c/
#include "lib9.h" #include "isa.h" #include "interp.h" #include "raise.h" #define OP(fn) void fn(void) #define W(p) *((WORD*)(p)) #define CANGET(c) ((c)->size > 0) #define CANPUT(c) ((c)->buf != H && (c)->size < (c)->buf->len) extern OP(isend); extern OP(irecv); /* * Count the number of ready channels in an array of channels * Set each channel's alt pointer to the owning prog */ static int altmark(Channel *c, Prog *p) { int nrdy; Array *a; Channel **ca, **ec; nrdy = 0; a = (Array*)c; ca = (Channel**)a->data; ec = ca + a->len; while(ca < ec) { c = *ca; if(c != H) { if(c->send->prog || CANGET(c)) nrdy++; cqadd(&c->recv, p); } ca++; } return nrdy; } /* * Remove alt references to an array of channels */ static void altunmark(Channel *c, WORD *ptr, Prog *p, int sr, Channel **sel, int dn) { int n; Array *a; Channel **ca, **ec; n = 0; a = (Array*)c; ca = (Channel**)a->data; ec = ca + a->len; while(ca < ec) { c = *ca; if(c != H && c->recv->prog) cqdelp(&c->recv, p); if(sr == 1 && *sel == c) { W(p->R.d) = dn; p->ptr = ptr + 1; ptr[0] = n; *sel = nil; } ca++; n++; } } /* * ALT Pass 1 - Count the number of ready channels and mark * each channel as ALT by this prog */ static int altrdy(Alt *a, Prog *p) { char *e; Type *t; int nrdy; Channel *c; Altc *ac, *eac; e = nil; nrdy = 0; ac = a->ac + a->nsend; eac = ac + a->nrecv; while(ac < eac) { c = ac->c; ac++; if(c == H) { e = exNilref; continue; } t = D2H(c)->t; if(t == &Tarray) nrdy += altmark(c, p); else { if(c->send->prog || CANGET(c)) nrdy++; cqadd(&c->recv, p); } } ac = a->ac; eac = ac + a->nsend; while(ac < eac) { c = ac->c; ac++; if(c == H) { e = exNilref; continue; } if(c->recv->prog || CANPUT(c)) { if(c->recv->prog == p) { e = exAlt; continue; } nrdy++; } cqadd(&c->send, p); } if(e != nil) { altdone(a, p, nil, -1); error(e); } return nrdy; } /* * ALT Pass 3 - Pull out of an ALT cancelling the channel pointers in each item */ void altdone(Alt *a, Prog *p, Channel *sel, int sr) { int n; Type *t; Channel *c; Altc *ac, *eac; n = 0; ac = a->ac; eac = a->ac + a->nsend; while(ac < eac) { c = ac->c; if(c != H) { if(c->send->prog) cqdelp(&c->send, p); if(sr == 0 && c == sel) { p->ptr = ac->ptr; W(p->R.d) = n; sel = nil; } } ac++; n++; } eac = a->ac + a->nsend + a->nrecv; while(ac < eac) { c = ac->c; if(c != H) { t = D2H(c)->t; if(t == &Tarray) altunmark(c, ac->ptr, p, sr, &sel, n); else { if(c->recv->prog) cqdelp(&c->recv, p); if(sr == 1 && c == sel) { p->ptr = ac->ptr; W(p->R.d) = n; sel = nil; } } } ac++; n++; } } /* * ALT Pass 2 - Perform the communication on the chosen channel */ static void altcomm(Alt *a, int which) { Type *t; Array *r; int n, an; WORD *ptr; Altc *ac, *eac; Channel *c, **ca, **ec; n = 0; ac = a->ac; eac = ac + a->nsend; while(ac < eac) { c = ac->c; if((c->recv->prog != nil || CANPUT(c)) && which-- == 0) { W(R.d) = n; R.s = ac->ptr; R.d = &c; isend(); return; } ac++; n++; } eac = eac + a->nrecv; while(ac < eac) { c = ac->c; t = D2H(c)->t; if(t == &Tarray) { an = 0; r = (Array*)c; ca = (Channel**)r->data; ec = ca + r->len; while(ca < ec) { c = *ca; if(c != H && (c->send->prog != nil || CANGET(c)) && which-- == 0) { W(R.d) = n; R.s = &c; ptr = ac->ptr; R.d = ptr + 1; ptr[0] = an; irecv(); return; } ca++; an++; } } else if((c->send->prog != nil || CANGET(c)) && which-- == 0) { W(R.d) = n; R.s = &c; R.d = ac->ptr; irecv(); return; } ac++; n++; } return; } void altgone(Prog *p) { Alt *a; if (p->state == Palt) { a = p->R.s; altdone(a, p, nil, -1); p->kill = "alt channel hungup"; addrun(p); } } void xecalt(int block) { Alt *a; Prog *p; int nrdy; static ulong xrand = 0x20342; p = currun(); a = R.s; nrdy = altrdy(a, p); if(nrdy == 0) { if(block) { delrun(Palt); p->R.s = R.s; p->R.d = R.d; R.IC = 1; R.t = 1; return; } W(R.d) = a->nsend + a->nrecv; altdone(a, p, nil, -1); return; } xrand = xrand*1103515245 + 12345; altcomm(a, (xrand>>8)%nrdy); altdone(a, p, nil, -1); }