blob: 4dc51fcf43423b5b45d0381becdfbe2dd6be92eb [file] [log] [blame]
rsc619085f2004-12-25 21:57:50 +00001#include "threadimpl.h"
2
3/*
4 * One can go through a lot of effort to avoid this global lock.
5 * You have to put locks in all the channels and all the Alt
6 * structures. At the beginning of an alt you have to lock all
7 * the channels, but then to try to actually exec an op you
8 * have to lock the other guy's alt structure, so that other
9 * people aren't trying to use him in some other op at the
10 * same time.
11 *
12 * For Plan 9 apps, it's just not worth the extra effort.
13 */
14static QLock chanlock;
15
16Channel*
17chancreate(int elemsize, int bufsize)
18{
19 Channel *c;
20
21 c = malloc(sizeof *c+bufsize*elemsize);
rsc73722a82004-12-27 03:49:03 +000022 if(c == nil)
23 sysfatal("chancreate malloc: %r");
rsc619085f2004-12-25 21:57:50 +000024 memset(c, 0, sizeof *c);
25 c->elemsize = elemsize;
26 c->bufsize = bufsize;
27 c->nbuf = 0;
28 c->buf = (uchar*)(c+1);
29 return c;
30}
31
32void
33chansetname(Channel *c, char *fmt, ...)
34{
35 char *name;
36 va_list arg;
37
38 va_start(arg, fmt);
39 name = vsmprint(fmt, arg);
40 va_end(arg);
41 free(c->name);
42 c->name = name;
43}
44
45/* bug - work out races */
46void
47chanfree(Channel *c)
48{
49 if(c == nil)
50 return;
51 free(c->name);
52 free(c->arecv.a);
53 free(c->asend.a);
54 free(c);
55}
56
57static void
58addarray(_Altarray *a, Alt *alt)
59{
60 if(a->n == a->m){
61 a->m += 16;
62 a->a = realloc(a->a, a->m*sizeof a->a[0]);
63 }
64 a->a[a->n++] = alt;
65}
66
67static void
68delarray(_Altarray *a, int i)
69{
70 --a->n;
71 a->a[i] = a->a[a->n];
72}
73
74/*
75 * doesn't really work for things other than CHANSND and CHANRCV
76 * but is only used as arg to chanarray, which can handle it
77 */
78#define otherop(op) (CHANSND+CHANRCV-(op))
79
80static _Altarray*
81chanarray(Channel *c, uint op)
82{
83 switch(op){
84 default:
85 return nil;
86 case CHANSND:
87 return &c->asend;
88 case CHANRCV:
89 return &c->arecv;
90 }
91}
92
93static int
94altcanexec(Alt *a)
95{
96 _Altarray *ar;
97 Channel *c;
98
99 if(a->op == CHANNOP)
100 return 0;
101 c = a->c;
102 if(c->bufsize == 0){
103 ar = chanarray(c, otherop(a->op));
104 return ar && ar->n;
105 }else{
106 switch(a->op){
107 default:
108 return 0;
109 case CHANSND:
110 return c->nbuf < c->bufsize;
111 case CHANRCV:
112 return c->nbuf > 0;
113 }
114 }
115}
116
117static void
118altqueue(Alt *a)
119{
120 _Altarray *ar;
121
122 ar = chanarray(a->c, a->op);
123 addarray(ar, a);
124}
125
126static void
127altdequeue(Alt *a)
128{
129 int i;
130 _Altarray *ar;
131
132 ar = chanarray(a->c, a->op);
133 if(ar == nil){
134 fprint(2, "bad use of altdequeue op=%d\n", a->op);
135 abort();
136 }
137
138 for(i=0; i<ar->n; i++)
139 if(ar->a[i] == a){
140 delarray(ar, i);
141 return;
142 }
143 fprint(2, "cannot find self in altdq\n");
144 abort();
145}
146
147static void
148altalldequeue(Alt *a)
149{
150 int i;
151
152 for(i=0; a[i].op!=CHANEND && a[i].op!=CHANNOBLK; i++)
153 if(a[i].op != CHANNOP)
154 altdequeue(&a[i]);
155}
156
157static void
158amove(void *dst, void *src, uint n)
159{
160 if(dst){
161 if(src == nil)
162 memset(dst, 0, n);
163 else
164 memmove(dst, src, n);
165 }
166}
167
168/*
169 * Actually move the data around. There are up to three
170 * players: the sender, the receiver, and the channel itself.
171 * If the channel is unbuffered or the buffer is empty,
172 * data goes from sender to receiver. If the channel is full,
173 * the receiver removes some from the channel and the sender
174 * gets to put some in.
175 */
176static void
177altcopy(Alt *s, Alt *r)
178{
179 Alt *t;
180 Channel *c;
181 uchar *cp;
182
183 /*
184 * Work out who is sender and who is receiver
185 */
186 if(s == nil && r == nil)
187 return;
188 assert(s != nil);
189 c = s->c;
190 if(s->op == CHANRCV){
191 t = s;
192 s = r;
193 r = t;
194 }
195 assert(s==nil || s->op == CHANSND);
196 assert(r==nil || r->op == CHANRCV);
197
198 /*
199 * Channel is empty (or unbuffered) - copy directly.
200 */
201 if(s && r && c->nbuf == 0){
202 amove(r->v, s->v, c->elemsize);
203 return;
204 }
205
206 /*
207 * Otherwise it's always okay to receive and then send.
208 */
209 if(r){
210 cp = c->buf + c->off*c->elemsize;
211 amove(r->v, cp, c->elemsize);
212 --c->nbuf;
213 if(++c->off == c->bufsize)
214 c->off = 0;
215 }
216 if(s){
217 cp = c->buf + (c->off+c->nbuf)%c->bufsize*c->elemsize;
218 amove(cp, s->v, c->elemsize);
219 ++c->nbuf;
220 }
221}
222
223static void
224altexec(Alt *a)
225{
226 int i;
227 _Altarray *ar;
228 Alt *other;
229 Channel *c;
230
231 c = a->c;
232 ar = chanarray(c, otherop(a->op));
233 if(ar && ar->n){
234 i = rand()%ar->n;
235 other = ar->a[i];
236 altcopy(a, other);
237 altalldequeue(other->xalt);
238 other->xalt[0].xalt = other;
239 _threadready(other->thread);
240 }else
241 altcopy(a, nil);
242}
243
244#define dbgalt 0
245int
246chanalt(Alt *a)
247{
248 int i, j, ncan, n, canblock;
249 Channel *c;
250 _Thread *t;
251
252 for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++)
253 ;
254 n = i;
255 canblock = a[i].op == CHANEND;
256
257 t = proc()->thread;
258 for(i=0; i<n; i++){
259 a[i].thread = t;
260 a[i].xalt = a;
261 }
262 qlock(&chanlock);
263if(dbgalt) print("alt ");
264 ncan = 0;
265 for(i=0; i<n; i++){
266 c = a[i].c;
267if(dbgalt) print(" %c:", "esrnb"[a[i].op]);
268if(dbgalt) if(c->name) print("%s", c->name); else print("%p", c);
269 if(altcanexec(&a[i])){
270if(dbgalt) print("*");
271 ncan++;
272 }
273 }
274 if(ncan){
275 j = rand()%ncan;
276 for(i=0; i<n; i++){
277 if(altcanexec(&a[i])){
278 if(j-- == 0){
279if(dbgalt){
280c = a[i].c;
281print(" => %c:", "esrnb"[a[i].op]);
282if(c->name) print("%s", c->name); else print("%p", c);
283print("\n");
284}
285 altexec(&a[i]);
286 qunlock(&chanlock);
287 return i;
288 }
289 }
290 }
291 }
292if(dbgalt)print("\n");
293
294 if(!canblock){
295 qunlock(&chanlock);
296 return -1;
297 }
298
299 for(i=0; i<n; i++){
300 if(a[i].op != CHANNOP)
301 altqueue(&a[i]);
302 }
303 qunlock(&chanlock);
304
305 _threadswitch();
306
307 /*
308 * the guy who ran the op took care of dequeueing us
309 * and then set a[0].alt to the one that was executed.
310 */
311 return a[0].xalt - a;
312}
313
314static int
315_chanop(Channel *c, int op, void *p, int canblock)
316{
317 Alt a[2];
318
319 a[0].c = c;
320 a[0].op = op;
321 a[0].v = p;
322 a[1].op = canblock ? CHANEND : CHANNOBLK;
323 if(chanalt(a) < 0)
324 return -1;
325 return 1;
326}
327
328int
329chansend(Channel *c, void *v)
330{
331 return _chanop(c, CHANSND, v, 1);
332}
333
334int
335channbsend(Channel *c, void *v)
336{
337 return _chanop(c, CHANSND, v, 0);
338}
339
340int
341chanrecv(Channel *c, void *v)
342{
343 return _chanop(c, CHANRCV, v, 1);
344}
345
346int
347channbrecv(Channel *c, void *v)
348{
349 return _chanop(c, CHANRCV, v, 0);
350}
351
352int
353chansendp(Channel *c, void *v)
354{
355 return _chanop(c, CHANSND, (void*)&v, 1);
356}
357
358void*
359chanrecvp(Channel *c)
360{
361 void *v;
362
363 _chanop(c, CHANRCV, (void*)&v, 1);
364 return v;
365}
366
367int
368channbsendp(Channel *c, void *v)
369{
370 return _chanop(c, CHANSND, (void*)&v, 0);
371}
372
373void*
374channbrecvp(Channel *c)
375{
376 void *v;
377
378 _chanop(c, CHANRCV, (void*)&v, 0);
379 return v;
380}
381
382int
383chansendul(Channel *c, ulong val)
384{
385 return _chanop(c, CHANSND, &val, 1);
386}
387
388ulong
389chanrecvul(Channel *c)
390{
391 ulong val;
392
393 _chanop(c, CHANRCV, &val, 1);
394 return val;
395}
396
397int
398channbsendul(Channel *c, ulong val)
399{
400 return _chanop(c, CHANSND, &val, 0);
401}
402
403ulong
404channbrecvul(Channel *c)
405{
406 ulong val;
407
408 _chanop(c, CHANRCV, &val, 0);
409 return val;
410}
411