int chanalt(Alt *a) { int i, j, ncan, n, canblock; Channel *c; Task *t; needstack(512); for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++) ; n = i; canblock = a[i].op == CHANEND; t = taskrunning; //所有alt指向正在运行的task for(i=0; i<n; i++){ a[i].task = t; a[i].xalt = a; } ncan = 0; for(i=0; i<n; i++){ if(altcanexec(&a[i])){ ncan++; } } if(ncan){ j = rand()%ncan; for(i=0; i<n; i++){ if(altcanexec(&a[i])){ if(j-- == 0){ altexec(&a[i]); return i; } } } } if(!canblock) return -1; for(i=0; i<n; i++){ if(a[i].op != CHANNOP) altqueue(&a[i]); } taskswitch(); /* * the guy who ran the op took care of dequeueing us * and then set a[0].alt to the one that was executed. */ return a[0].xalt - a; }
int chanalt(Alt *a) { int i, j, ncan, n, canblock; Channel *c; Task *t; needstack(512); for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++) ; n = i; canblock = a[i].op == CHANEND; t = taskrunning; for(i=0; i<n; i++){ a[i].task = t; a[i].xalt = a; } if(dbgalt) print("alt "); ncan = 0; for(i=0; i<n; i++){ c = a[i].c; if(dbgalt) print(" %c:", "esrnb"[a[i].op]); if(dbgalt) { if(c->name) print("%s", c->name); else print("%p", c); } if(altcanexec(&a[i])){ if(dbgalt) print("*"); ncan++; } } if(ncan){ j = rand()%ncan; for(i=0; i<n; i++){ if(altcanexec(&a[i])){ if(j-- == 0){ if(dbgalt){ c = a[i].c; print(" => %c:", "esrnb"[a[i].op]); if(c->name) print("%s", c->name); else print("%p", c); print("\n"); } altexec(&a[i]); return i; } } } } if(dbgalt)print("\n"); if(!canblock) return -1; for(i=0; i<n; i++){ if(a[i].op != CHANNOP) altqueue(&a[i]); } taskswitch(); /* * the guy who ran the op took care of dequeueing us * and then set a[0].alt to the one that was executed. */ return a[0].xalt - a; }
int alt(Alt *alts) { Alt *a, *xa, *ca; Channel volatile *c; int n, s, waiting, allreadycl; void* r; Thread *t; /* * The point of going splhi here is that note handlers * might reasonably want to use channel operations, * but that will hang if the note comes while we hold the * chanlock. Instead, we delay the note until we've dropped * the lock. */ t = _threadgetproc()->thread; if(t->moribund || _threadexitsallstatus) yield(); /* won't return */ s = _procsplhi(); lock(&chanlock); t->alt = alts; t->chan = Chanalt; /* test whether any channels can proceed */ n = 0; a = nil; for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){ xa->entryno = -1; if(xa->op == CHANNOP) continue; c = xa->c; if(c==nil){ unlock(&chanlock); _procsplx(s); t->chan = Channone; return -1; } if(isopenfor(c, xa->op) && canexec(xa)) if(nrand(++n) == 0) a = xa; } if(a==nil){ /* nothing can proceed */ if(xa->op == CHANNOBLK){ unlock(&chanlock); _procsplx(s); t->chan = Channone; if(xa->op == CHANNOBLK) return xa - alts; } /* enqueue on all channels open for us. */ c = nil; ca = nil; waiting = 0; allreadycl = 0; for(xa=alts; xa->op!=CHANEND; xa++) if(xa->op==CHANNOP) continue; else if(isopenfor(xa->c, xa->op)){ waiting = 1; enqueue(xa, &c); } else if(xa->err != errcl) ca = xa; else allreadycl = 1; if(waiting == 0) if(ca != nil){ /* everything was closed, select last channel */ ca->err = errcl; unlock(&chanlock); _procsplx(s); t->chan = Channone; return ca - alts; } else if(allreadycl){ /* everything was already closed */ unlock(&chanlock); _procsplx(s); t->chan = Channone; return -1; } /* * wait for successful rendezvous. * we can't just give up if the rendezvous * is interrupted -- someone else might come * along and try to rendezvous with us, so * we need to be here. * if the channel was closed, the op is done * and we flag an error for the entry. */ Again: unlock(&chanlock); _procsplx(s); r = _threadrendezvous(&c, 0); s = _procsplhi(); lock(&chanlock); if(r==Intred){ /* interrupted */ if(c!=nil) /* someone will meet us; go back */ goto Again; c = (Channel*)~0; /* so no one tries to meet us */ } /* dequeue from channels, find selected one */ a = nil; for(xa=alts; xa->op!=CHANEND; xa++){ if(xa->op==CHANNOP) continue; if(xa->c == c){ a = xa; a->err = nil; if(r == Closed) a->err = errcl; } dequeue(xa); } unlock(&chanlock); _procsplx(s); if(a == nil){ /* we were interrupted */ assert(c==(Channel*)~0); return -1; } }else altexec(a, s); /* unlocks chanlock, does splx */ _sched(); t->chan = Channone; return a - alts; }