xref: /inferno-os/libinterp/alt.c (revision 2a409d9c503f875c5ba694c0c601b287876c3536)
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