diff options
Diffstat (limited to 'src/pkg/runtime/chan.c')
-rw-r--r-- | src/pkg/runtime/chan.c | 1024 |
1 files changed, 1024 insertions, 0 deletions
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c new file mode 100644 index 000000000..be65bcbc1 --- /dev/null +++ b/src/pkg/runtime/chan.c @@ -0,0 +1,1024 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" + +static int32 debug = 0; +static Lock chanlock; + +enum +{ + Wclosed = 0x0001, // writer has closed + Rclosed = 0x0002, // reader has seen close + Eincr = 0x0004, // increment errors + Emax = 0x0800, // error limit before throw +}; + +typedef struct Hchan Hchan; +typedef struct Link Link; +typedef struct WaitQ WaitQ; +typedef struct SudoG SudoG; +typedef struct Select Select; +typedef struct Scase Scase; + +struct SudoG +{ + G* g; // g and selgen constitute + int32 selgen; // a weak pointer to g + int16 offset; // offset of case number + int8 isfree; // offset of case number + SudoG* link; + byte elem[8]; // synch data element (+ more) +}; + +struct WaitQ +{ + SudoG* first; + SudoG* last; +}; + +struct Hchan +{ + uint16 elemsize; + uint16 closed; // Wclosed Rclosed errorcount + uint32 dataqsiz; // size of the circular q + uint32 qcount; // total data in the q + Alg* elemalg; // interface for element type + Link* senddataq; // pointer for sender + Link* recvdataq; // pointer for receiver + WaitQ recvq; // list of recv waiters + WaitQ sendq; // list of send waiters + SudoG* free; // freelist +}; + +struct Link +{ + Link* link; // asynch queue circular linked list + byte elem[8]; // asynch queue data element (+ more) +}; + +struct Scase +{ + Hchan* chan; // chan + byte* pc; // return pc + uint16 send; // 0-recv 1-send 2-default + uint16 so; // vararg of selected bool + union { + byte elem[8]; // element (send) + byte* elemp; // pointer to element (recv) + } u; +}; + +struct Select +{ + uint16 tcase; // total count of scase[] + uint16 ncase; // currently filled scase[] + Select* link; // for freelist + Scase* scase[1]; // one per case +}; + +static Select* selfree[20]; + +static SudoG* dequeue(WaitQ*, Hchan*); +static void enqueue(WaitQ*, SudoG*); +static SudoG* allocsg(Hchan*); +static void freesg(Hchan*, SudoG*); +static uint32 gcd(uint32, uint32); +static uint32 fastrand1(void); +static uint32 fastrand2(void); + +// newchan(elemsize uint32, elemalg uint32, hint uint32) (hchan *chan any); +void +sys·newchan(uint32 elemsize, uint32 elemalg, uint32 hint, + Hchan* ret) +{ + Hchan *c; + int32 i; + + if(elemalg >= nelem(algarray)) { + printf("chan(alg=%d)\n", elemalg); + throw("sys·newchan: unsupported elem type"); + } + + c = mal(sizeof(*c)); + + c->elemsize = elemsize; + c->elemalg = &algarray[elemalg]; + + if(hint > 0) { + Link *d, *b, *e; + + // make a circular q + b = nil; + e = nil; + for(i=0; i<hint; i++) { + d = mal(sizeof(*d) + c->elemsize - sizeof(d->elem)); + if(e == nil) + e = d; + d->link = b; + b = d; + } + e->link = b; + c->recvdataq = b; + c->senddataq = b; + c->qcount = 0; + c->dataqsiz = hint; + } + + ret = c; + FLUSH(&ret); + + if(debug) { + prints("newchan: chan="); + sys·printpointer(c); + prints("; elemsize="); + sys·printint(elemsize); + prints("; elemalg="); + sys·printint(elemalg); + prints("; dataqsiz="); + sys·printint(c->dataqsiz); + prints("\n"); + } +} + +static void +incerr(Hchan* c) +{ + c->closed += Eincr; + if(c->closed & Emax) { + unlock(&chanlock); + throw("too many operations on a closed channel"); + } +} + +/* + * generic single channel send/recv + * if the bool pointer is nil, + * then the full exchange will + * occur. if pres is not nil, + * then the protocol will not + * sleep but return if it could + * not complete + */ +void +sendchan(Hchan *c, byte *ep, bool *pres) +{ + SudoG *sg; + G* gp; + + if(debug) { + prints("chansend: chan="); + sys·printpointer(c); + prints("; elem="); + c->elemalg->print(c->elemsize, ep); + prints("\n"); + } + + lock(&chanlock); +loop: + if(c->closed & Wclosed) + goto closed; + + if(c->dataqsiz > 0) + goto asynch; + + sg = dequeue(&c->recvq, c); + if(sg != nil) { + if(ep != nil) + c->elemalg->copy(c->elemsize, sg->elem, ep); + + gp = sg->g; + gp->param = sg; + unlock(&chanlock); + ready(gp); + + if(pres != nil) + *pres = true; + return; + } + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + + sg = allocsg(c); + if(ep != nil) + c->elemalg->copy(c->elemsize, sg->elem, ep); + g->param = nil; + g->status = Gwaiting; + enqueue(&c->sendq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + freesg(c, sg); + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +asynch: + if(c->closed & Wclosed) + goto closed; + + if(c->qcount >= c->dataqsiz) { + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + sg = allocsg(c); + g->status = Gwaiting; + enqueue(&c->sendq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + goto asynch; + } + if(ep != nil) + c->elemalg->copy(c->elemsize, c->senddataq->elem, ep); + c->senddataq = c->senddataq->link; + c->qcount++; + + sg = dequeue(&c->recvq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + unlock(&chanlock); + ready(gp); + } else + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +closed: + incerr(c); + if(pres != nil) + *pres = true; + unlock(&chanlock); +} + +static void +chanrecv(Hchan* c, byte *ep, bool* pres) +{ + SudoG *sg; + G *gp; + + if(debug) { + prints("chanrecv: chan="); + sys·printpointer(c); + prints("\n"); + } + + lock(&chanlock); +loop: + if(c->dataqsiz > 0) + goto asynch; + + if(c->closed & Wclosed) + goto closed; + + sg = dequeue(&c->sendq, c); + if(sg != nil) { + c->elemalg->copy(c->elemsize, ep, sg->elem); + + gp = sg->g; + gp->param = sg; + unlock(&chanlock); + ready(gp); + + if(pres != nil) + *pres = true; + return; + } + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + + sg = allocsg(c); + g->param = nil; + g->status = Gwaiting; + enqueue(&c->recvq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + + c->elemalg->copy(c->elemsize, ep, sg->elem); + freesg(c, sg); + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +asynch: + if(c->qcount <= 0) { + if(c->closed & Wclosed) + goto closed; + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + sg = allocsg(c); + g->status = Gwaiting; + enqueue(&c->recvq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + goto asynch; + } + c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem); + c->recvdataq = c->recvdataq->link; + c->qcount--; + sg = dequeue(&c->sendq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + unlock(&chanlock); + ready(gp); + if(pres != nil) + *pres = true; + return; + } + + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +closed: + c->elemalg->copy(c->elemsize, ep, nil); + c->closed |= Rclosed; + incerr(c); + if(pres != nil) + *pres = true; + unlock(&chanlock); +} + +// chansend1(hchan *chan any, elem any); +void +sys·chansend1(Hchan* c, ...) +{ + int32 o; + byte *ae; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + sendchan(c, ae, nil); +} + +// chansend2(hchan *chan any, elem any) (pres bool); +void +sys·chansend2(Hchan* c, ...) +{ + int32 o; + byte *ae, *ap; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + o = rnd(o+c->elemsize, 1); + ap = (byte*)&c + o; + + sendchan(c, ae, ap); +} + +// chanrecv1(hchan *chan any) (elem any); +void +sys·chanrecv1(Hchan* c, ...) +{ + int32 o; + byte *ae; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + + chanrecv(c, ae, nil); +} + +// chanrecv2(hchan *chan any) (elem any, pres bool); +void +sys·chanrecv2(Hchan* c, ...) +{ + int32 o; + byte *ae, *ap; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + o = rnd(o+c->elemsize, 1); + ap = (byte*)&c + o; + + chanrecv(c, ae, ap); +} + +// chanrecv3(hchan *chan any, elem *any) (pres bool); +void +sys·chanrecv3(Hchan* c, byte* ep, byte pres) +{ + chanrecv(c, ep, &pres); +} + +// newselect(size uint32) (sel *byte); +void +sys·newselect(int32 size, Select *sel) +{ + int32 n; + + n = 0; + if(size > 1) + n = size-1; + + lock(&chanlock); + sel = nil; + if(size >= 1 && size < nelem(selfree)) { + sel = selfree[size]; + if(sel != nil) + selfree[size] = sel->link; + } + unlock(&chanlock); + if(sel == nil) + sel = mal(sizeof(*sel) + n*sizeof(sel->scase[0])); + + sel->tcase = size; + sel->ncase = 0; + FLUSH(&sel); + if(debug) { + prints("newselect s="); + sys·printpointer(sel); + prints(" size="); + sys·printint(size); + prints("\n"); + } +} + +// selectsend(sel *byte, hchan *chan any, elem any) (selected bool); +void +sys·selectsend(Select *sel, Hchan *c, ...) +{ + int32 i, eo; + Scase *cas; + byte *ae; + + // nil cases do not compete + if(c == nil) + return; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectsend: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas + c->elemsize - sizeof(cas->u.elem)); + sel->scase[i] = cas; + } + + cas->pc = sys·getcallerpc(&sel); + cas->chan = c; + + eo = rnd(sizeof(sel), sizeof(c)); + eo = rnd(eo+sizeof(c), c->elemsize); + cas->so = rnd(eo+c->elemsize, 1); + cas->send = 1; + + ae = (byte*)&sel + eo; + c->elemalg->copy(c->elemsize, cas->u.elem, ae); + + if(debug) { + prints("selectsend s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" chan="); + sys·printpointer(cas->chan); + prints(" po="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + +// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); +void +sys·selectrecv(Select *sel, Hchan *c, ...) +{ + int32 i, eo; + Scase *cas; + + // nil cases do not compete + if(c == nil) + return; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectrecv: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas); + sel->scase[i] = cas; + } + cas->pc = sys·getcallerpc(&sel); + cas->chan = c; + + eo = rnd(sizeof(sel), sizeof(c)); + eo = rnd(eo+sizeof(c), sizeof(byte*)); + cas->so = rnd(eo+sizeof(byte*), 1); + cas->send = 0; + cas->u.elemp = *(byte**)((byte*)&sel + eo); + + if(debug) { + prints("selectrecv s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" chan="); + sys·printpointer(cas->chan); + prints(" so="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + + +// selectdefaul(sel *byte) (selected bool); +void +sys·selectdefault(Select *sel, ...) +{ + int32 i; + Scase *cas; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectdefault: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas); + sel->scase[i] = cas; + } + cas->pc = sys·getcallerpc(&sel); + cas->chan = nil; + + cas->so = rnd(sizeof(sel), 1); + cas->send = 2; + cas->u.elemp = nil; + + if(debug) { + prints("selectdefault s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" so="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + +// selectgo(sel *byte); +void +sys·selectgo(Select *sel) +{ + uint32 p, o, i; + Scase *cas, *dfl; + Hchan *c; + SudoG *sg; + G *gp; + byte *as; + + if(debug) { + prints("selectgo: sel="); + sys·printpointer(sel); + prints("\n"); + } + + if(sel->ncase < 2) { + if(sel->ncase < 1) + throw("selectgo: no cases"); + // make special case of one. + } + + // select a (relative) prime + for(i=0;; i++) { + p = fastrand1(); + if(gcd(p, sel->ncase) == 1) + break; + if(i > 1000) { + throw("selectgo: failed to select prime"); + } + } + + // select an initial offset + o = fastrand2(); + + p %= sel->ncase; + o %= sel->ncase; + + lock(&chanlock); + +loop: + // pass 1 - look for something already waiting + dfl = nil; + for(i=0; i<sel->ncase; i++) { + cas = sel->scase[o]; + + if(cas->send == 2) { // default + dfl = cas; + goto next1; + } + + c = cas->chan; + if(c->dataqsiz > 0) { + if(cas->send) { + if(c->closed & Wclosed) + goto sclose; + if(c->qcount < c->dataqsiz) + goto asyns; + goto next1; + } + if(c->qcount > 0) + goto asynr; + if(c->closed & Wclosed) + goto rclose; + goto next1; + } + + if(cas->send) { + if(c->closed & Wclosed) + goto sclose; + sg = dequeue(&c->recvq, c); + if(sg != nil) + goto gots; + goto next1; + } + sg = dequeue(&c->sendq, c); + if(sg != nil) + goto gotr; + if(c->closed & Wclosed) + goto rclose; + + next1: + o += p; + if(o >= sel->ncase) + o -= sel->ncase; + } + + if(dfl != nil) { + cas = dfl; + goto retc; + } + + + // pass 2 - enqueue on all chans + for(i=0; i<sel->ncase; i++) { + cas = sel->scase[o]; + c = cas->chan; + + if(c->dataqsiz > 0) { + if(cas->send) { + if(c->qcount < c->dataqsiz) { + prints("selectgo: pass 2 async send\n"); + goto asyns; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->sendq, sg); + goto next2; + } + if(c->qcount > 0) { + prints("selectgo: pass 2 async recv\n"); + goto asynr; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->recvq, sg); + goto next2; + } + + if(cas->send) { + sg = dequeue(&c->recvq, c); + if(sg != nil) { + prints("selectgo: pass 2 sync send\n"); + g->selgen++; + goto gots; + } + sg = allocsg(c); + sg->offset = o; + c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); + enqueue(&c->sendq, sg); + goto next2; + } + sg = dequeue(&c->sendq, c); + if(sg != nil) { + prints("selectgo: pass 2 sync recv\n"); + g->selgen++; + goto gotr; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->recvq, sg); + + next2: + o += p; + if(o >= sel->ncase) + o -= sel->ncase; + } + + g->param = nil; + g->status = Gwaiting; + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + + o = sg->offset; + cas = sel->scase[o]; + c = cas->chan; + + if(c->dataqsiz > 0) { +// prints("shouldnt happen\n"); + goto loop; + } + + if(debug) { + prints("wait-return: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" cas="); + sys·printpointer(cas); + prints(" send="); + sys·printint(cas->send); + prints(" o="); + sys·printint(o); + prints("\n"); + } + + if(!cas->send) { + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem); + } + + freesg(c, sg); + goto retc; + +asynr: + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, c->recvdataq->elem); + c->recvdataq = c->recvdataq->link; + c->qcount--; + sg = dequeue(&c->sendq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + ready(gp); + } + goto retc; + +asyns: + if(cas->u.elem != nil) + c->elemalg->copy(c->elemsize, c->senddataq->elem, cas->u.elem); + c->senddataq = c->senddataq->link; + c->qcount++; + sg = dequeue(&c->recvq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + ready(gp); + } + goto retc; + +gotr: + // recv path to wakeup the sender (sg) + if(debug) { + prints("gotr: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" o="); + sys·printint(o); + prints("\n"); + } + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem); + gp = sg->g; + gp->param = sg; + ready(gp); + goto retc; + +rclose: + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, nil); + c->closed |= Rclosed; + incerr(c); + goto retc; + +gots: + // send path to wakeup the receiver (sg) + if(debug) { + prints("gots: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" o="); + sys·printint(o); + prints("\n"); + } + if(c->closed & Wclosed) + goto sclose; + c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); + gp = sg->g; + gp->param = sg; + ready(gp); + goto retc; + +sclose: + incerr(c); + goto retc; + +retc: + if(sel->ncase >= 1 && sel->ncase < nelem(selfree)) { + sel->link = selfree[sel->ncase]; + selfree[sel->ncase] = sel; + } + unlock(&chanlock); + + sys·setcallerpc(&sel, cas->pc); + as = (byte*)&sel + cas->so; + *as = true; +} + +// closechan(sel *byte); +void +sys·closechan(Hchan *c) +{ + SudoG *sg; + G* gp; + + lock(&chanlock); + incerr(c); + c->closed |= Wclosed; + + // release all readers + for(;;) { + sg = dequeue(&c->recvq, c); + if(sg == nil) + break; + gp = sg->g; + gp->param = nil; + freesg(c, sg); + ready(gp); + } + + // release all writers + for(;;) { + sg = dequeue(&c->sendq, c); + if(sg == nil) + break; + gp = sg->g; + gp->param = nil; + freesg(c, sg); + ready(gp); + } + + unlock(&chanlock); +} + +// closedchan(sel *byte) bool; +void +sys·closedchan(Hchan *c, bool closed) +{ + // test Rclosed + closed = 0; + if(c->closed & Rclosed) + closed = 1; + FLUSH(&closed); +} + +static SudoG* +dequeue(WaitQ *q, Hchan *c) +{ + SudoG *sgp; + +loop: + sgp = q->first; + if(sgp == nil) + return nil; + q->first = sgp->link; + + // if sgp is stale, ignore it + if(sgp->selgen != sgp->g->selgen) { + //prints("INVALID PSEUDOG POINTER\n"); + freesg(c, sgp); + goto loop; + } + + // invalidate any others + sgp->g->selgen++; + return sgp; +} + +static void +enqueue(WaitQ *q, SudoG *sgp) +{ + sgp->link = nil; + if(q->first == nil) { + q->first = sgp; + q->last = sgp; + return; + } + q->last->link = sgp; + q->last = sgp; +} + +static SudoG* +allocsg(Hchan *c) +{ + SudoG* sg; + + sg = c->free; + if(sg != nil) { + c->free = sg->link; + } else + sg = mal(sizeof(*sg) + c->elemsize - sizeof(sg->elem)); + sg->selgen = g->selgen; + sg->g = g; + sg->offset = 0; + sg->isfree = 0; + + return sg; +} + +static void +freesg(Hchan *c, SudoG *sg) +{ + if(sg != nil) { + if(sg->isfree) + throw("chan.freesg: already free"); + sg->isfree = 1; + sg->link = c->free; + c->free = sg; + } +} + +static uint32 +gcd(uint32 u, uint32 v) +{ + for(;;) { + if(u > v) { + if(v == 0) + return u; + u = u%v; + continue; + } + if(u == 0) + return v; + v = v%u; + } +} + +static uint32 +fastrand1(void) +{ + static uint32 x = 0x49f6428aUL; + + x += x; + if(x & 0x80000000L) + x ^= 0x88888eefUL; + return x; +} + +static uint32 +fastrand2(void) +{ + static uint32 x = 0x49f6428aUL; + + x += x; + if(x & 0x80000000L) + x ^= 0xfafd871bUL; + return x; +} |