summaryrefslogtreecommitdiff
path: root/libinterp/alt.c
diff options
context:
space:
mode:
Diffstat (limited to 'libinterp/alt.c')
-rw-r--r--libinterp/alt.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/libinterp/alt.c b/libinterp/alt.c
new file mode 100644
index 00000000..61bda2e3
--- /dev/null
+++ b/libinterp/alt.c
@@ -0,0 +1,297 @@
+#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 int xrand = -1;
+
+ 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;
+ if(xrand < 0)
+ xrand ^= 0x88888EEF;
+
+ altcomm(a, xrand%nrdy);
+ altdone(a, p, nil, -1);
+}