1 #include "lib9.h"
2 #include "isa.h"
3 #include "interp.h"
4 #include "raise.h"
5
6 #define OP(fn) void fn(void)
7 #define W(p) *((WORD*)(p))
8
9 #define CANGET(c) ((c)->size > 0)
10 #define CANPUT(c) ((c)->buf != H && (c)->size < (c)->buf->len)
11
12 extern OP(isend);
13 extern OP(irecv);
14
15 /*
16 * Count the number of ready channels in an array of channels
17 * Set each channel's alt pointer to the owning prog
18 */
19 static int
altmark(Channel * c,Prog * p)20 altmark(Channel *c, Prog *p)
21 {
22 int nrdy;
23 Array *a;
24 Channel **ca, **ec;
25
26 nrdy = 0;
27 a = (Array*)c;
28 ca = (Channel**)a->data;
29 ec = ca + a->len;
30 while(ca < ec) {
31 c = *ca;
32 if(c != H) {
33 if(c->send->prog || CANGET(c))
34 nrdy++;
35 cqadd(&c->recv, p);
36 }
37 ca++;
38 }
39
40 return nrdy;
41 }
42
43 /*
44 * Remove alt references to an array of channels
45 */
46 static void
altunmark(Channel * c,WORD * ptr,Prog * p,int sr,Channel ** sel,int dn)47 altunmark(Channel *c, WORD *ptr, Prog *p, int sr, Channel **sel, int dn)
48 {
49 int n;
50 Array *a;
51 Channel **ca, **ec;
52
53 n = 0;
54 a = (Array*)c;
55 ca = (Channel**)a->data;
56 ec = ca + a->len;
57 while(ca < ec) {
58 c = *ca;
59 if(c != H && c->recv->prog)
60 cqdelp(&c->recv, p);
61 if(sr == 1 && *sel == c) {
62 W(p->R.d) = dn;
63 p->ptr = ptr + 1;
64 ptr[0] = n;
65 *sel = nil;
66 }
67 ca++;
68 n++;
69 }
70 }
71
72 /*
73 * ALT Pass 1 - Count the number of ready channels and mark
74 * each channel as ALT by this prog
75 */
76 static int
altrdy(Alt * a,Prog * p)77 altrdy(Alt *a, Prog *p)
78 {
79 char *e;
80 Type *t;
81 int nrdy;
82 Channel *c;
83 Altc *ac, *eac;
84
85 e = nil;
86 nrdy = 0;
87
88 ac = a->ac + a->nsend;
89 eac = ac + a->nrecv;
90 while(ac < eac) {
91 c = ac->c;
92 ac++;
93 if(c == H) {
94 e = exNilref;
95 continue;
96 }
97 t = D2H(c)->t;
98 if(t == &Tarray)
99 nrdy += altmark(c, p);
100 else {
101 if(c->send->prog || CANGET(c))
102 nrdy++;
103 cqadd(&c->recv, p);
104 }
105 }
106
107 ac = a->ac;
108 eac = ac + a->nsend;
109 while(ac < eac) {
110 c = ac->c;
111 ac++;
112 if(c == H) {
113 e = exNilref;
114 continue;
115 }
116 if(c->recv->prog || CANPUT(c)) {
117 if(c->recv->prog == p) {
118 e = exAlt;
119 continue;
120 }
121 nrdy++;
122 }
123 cqadd(&c->send, p);
124 }
125
126 if(e != nil) {
127 altdone(a, p, nil, -1);
128 error(e);
129 }
130
131 return nrdy;
132 }
133
134 /*
135 * ALT Pass 3 - Pull out of an ALT cancelling the channel pointers in each item
136 */
137 void
altdone(Alt * a,Prog * p,Channel * sel,int sr)138 altdone(Alt *a, Prog *p, Channel *sel, int sr)
139 {
140 int n;
141 Type *t;
142 Channel *c;
143 Altc *ac, *eac;
144
145 n = 0;
146 ac = a->ac;
147 eac = a->ac + a->nsend;
148 while(ac < eac) {
149 c = ac->c;
150 if(c != H) {
151 if(c->send->prog)
152 cqdelp(&c->send, p);
153 if(sr == 0 && c == sel) {
154 p->ptr = ac->ptr;
155 W(p->R.d) = n;
156 sel = nil;
157 }
158 }
159 ac++;
160 n++;
161 }
162
163 eac = a->ac + a->nsend + a->nrecv;
164 while(ac < eac) {
165 c = ac->c;
166 if(c != H) {
167 t = D2H(c)->t;
168 if(t == &Tarray)
169 altunmark(c, ac->ptr, p, sr, &sel, n);
170 else {
171 if(c->recv->prog)
172 cqdelp(&c->recv, p);
173 if(sr == 1 && c == sel) {
174 p->ptr = ac->ptr;
175 W(p->R.d) = n;
176 sel = nil;
177 }
178 }
179 }
180 ac++;
181 n++;
182 }
183 }
184
185 /*
186 * ALT Pass 2 - Perform the communication on the chosen channel
187 */
188 static void
altcomm(Alt * a,int which)189 altcomm(Alt *a, int which)
190 {
191 Type *t;
192 Array *r;
193 int n, an;
194 WORD *ptr;
195 Altc *ac, *eac;
196 Channel *c, **ca, **ec;
197
198 n = 0;
199 ac = a->ac;
200 eac = ac + a->nsend;
201 while(ac < eac) {
202 c = ac->c;
203 if((c->recv->prog != nil || CANPUT(c)) && which-- == 0) {
204 W(R.d) = n;
205 R.s = ac->ptr;
206 R.d = &c;
207 isend();
208 return;
209 }
210 ac++;
211 n++;
212 }
213
214 eac = eac + a->nrecv;
215 while(ac < eac) {
216 c = ac->c;
217 t = D2H(c)->t;
218 if(t == &Tarray) {
219 an = 0;
220 r = (Array*)c;
221 ca = (Channel**)r->data;
222 ec = ca + r->len;
223 while(ca < ec) {
224 c = *ca;
225 if(c != H && (c->send->prog != nil || CANGET(c)) && which-- == 0) {
226 W(R.d) = n;
227 R.s = &c;
228 ptr = ac->ptr;
229 R.d = ptr + 1;
230 ptr[0] = an;
231 irecv();
232 return;
233 }
234 ca++;
235 an++;
236 }
237 }
238 else
239 if((c->send->prog != nil || CANGET(c)) && which-- == 0) {
240 W(R.d) = n;
241 R.s = &c;
242 R.d = ac->ptr;
243 irecv();
244 return;
245 }
246 ac++;
247 n++;
248 }
249 return;
250 }
251
252 void
altgone(Prog * p)253 altgone(Prog *p)
254 {
255 Alt *a;
256
257 if (p->state == Palt) {
258 a = p->R.s;
259 altdone(a, p, nil, -1);
260 p->kill = "alt channel hungup";
261 addrun(p);
262 }
263 }
264
265 void
xecalt(int block)266 xecalt(int block)
267 {
268 Alt *a;
269 Prog *p;
270 int nrdy;
271 static ulong xrand = 0x20342;
272
273 p = currun();
274
275 a = R.s;
276 nrdy = altrdy(a, p);
277 if(nrdy == 0) {
278 if(block) {
279 delrun(Palt);
280 p->R.s = R.s;
281 p->R.d = R.d;
282 R.IC = 1;
283 R.t = 1;
284 return;
285 }
286 W(R.d) = a->nsend + a->nrecv;
287 altdone(a, p, nil, -1);
288 return;
289 }
290
291 xrand = xrand*1103515245 + 12345;
292 altcomm(a, (xrand>>8)%nrdy);
293 altdone(a, p, nil, -1);
294 }
295