| // 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; |
| |
| typedef struct Hchan Hchan; |
| typedef struct Link Link; |
| typedef struct WaitQ WaitQ; |
| typedef struct SudoG SudoG; |
| |
| struct SudoG |
| { |
| G* g; // g and selgen constitute |
| int64 selgen; // a weak pointer to g |
| SudoG* link; |
| }; |
| |
| struct WaitQ |
| { |
| SudoG* first; |
| SudoG* last; |
| }; |
| |
| struct Hchan |
| { |
| uint32 elemsize; |
| uint32 dataqsiz; // size of the circular q |
| uint32 qcount; // total data in the q |
| uint32 eo; // vararg of element |
| uint32 po; // vararg of present bool |
| 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; |
| byte elem[8]; |
| }; |
| |
| static SudoG* dequeue(WaitQ*, Hchan*); |
| static void enqueue(WaitQ*, SudoG*); |
| static SudoG* allocsg(Hchan*); |
| static void freesg(Hchan*, SudoG*); |
| |
| // 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)) { |
| prints("0<="); |
| sys·printint(elemalg); |
| prints("<"); |
| sys·printint(nelem(algarray)); |
| prints("\n"); |
| |
| throw("sys·newchan: elem algorithm out of range"); |
| } |
| |
| 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)); |
| 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; |
| } |
| |
| // these calculations are compiler dependent |
| c->eo = rnd(sizeof(c), elemsize); |
| c->po = rnd(c->eo+elemsize, 1); |
| |
| 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"); |
| } |
| } |
| |
| // chansend1(hchan *chan any, elem any); |
| void |
| sys·chansend1(Hchan* c, ...) |
| { |
| byte *ae; |
| SudoG *sgr; |
| G* gr; |
| |
| ae = (byte*)&c + c->eo; |
| if(debug) { |
| prints("chansend: chan="); |
| sys·printpointer(c); |
| prints("; elem="); |
| c->elemalg->print(c->elemsize, ae); |
| prints("\n"); |
| } |
| if(c->dataqsiz > 0) |
| goto asynch; |
| |
| sgr = dequeue(&c->recvq, c); |
| if(sgr != nil) { |
| gr = sgr->g; |
| freesg(c, sgr); |
| |
| c->elemalg->copy(c->elemsize, gr->elem, ae); |
| gr->status = Grunnable; |
| return; |
| } |
| |
| c->elemalg->copy(c->elemsize, g->elem, ae); |
| sgr = allocsg(c); |
| g->status = Gwaiting; |
| enqueue(&c->sendq, sgr); |
| sys·gosched(); |
| return; |
| |
| asynch: |
| while(c->qcount >= c->dataqsiz) { |
| sgr = allocsg(c); |
| g->status = Gwaiting; |
| enqueue(&c->sendq, sgr); |
| sys·gosched(); |
| } |
| c->elemalg->copy(c->elemsize, c->senddataq->elem, ae); |
| c->senddataq = c->senddataq->link; |
| c->qcount++; |
| sgr = dequeue(&c->recvq, c); |
| if(sgr != nil) { |
| gr = sgr->g; |
| freesg(c, sgr); |
| gr->status = Grunnable; |
| } |
| } |
| |
| // chansend2(hchan *chan any, elem any) (pres bool); |
| void |
| sys·chansend2(Hchan* c, ...) |
| { |
| byte *ae, *ap; |
| SudoG *sgr; |
| G *gr; |
| |
| ae = (byte*)&c + c->eo; |
| ap = (byte*)&c + c->po; |
| |
| if(debug) { |
| prints("chansend: chan="); |
| sys·printpointer(c); |
| prints("; elem="); |
| c->elemalg->print(c->elemsize, ae); |
| prints("\n"); |
| } |
| if(c->dataqsiz > 0) |
| goto asynch; |
| |
| sgr = dequeue(&c->recvq, c); |
| if(sgr != nil) { |
| gr = sgr->g; |
| freesg(c, sgr); |
| |
| c->elemalg->copy(c->elemsize, gr->elem, ae); |
| gr->status = Grunnable; |
| *ap = true; |
| return; |
| } |
| *ap = false; |
| return; |
| |
| asynch: |
| if(c->qcount >= c->dataqsiz) { |
| *ap = false; |
| return; |
| } |
| c->elemalg->copy(c->elemsize, c->senddataq->elem, ae); |
| c->senddataq = c->senddataq->link; |
| c->qcount++; |
| sgr = dequeue(&c->recvq, c); |
| if(gr != nil) { |
| gr = sgr->g; |
| freesg(c, sgr); |
| gr->status = Grunnable; |
| } |
| *ap = true; |
| } |
| |
| // chanrecv1(hchan *chan any) (elem any); |
| void |
| sys·chanrecv1(Hchan* c, ...) |
| { |
| byte *ae; |
| SudoG *sgs; |
| G *gs; |
| |
| ae = (byte*)&c + c->eo; |
| if(debug) { |
| prints("chanrecv1: chan="); |
| sys·printpointer(c); |
| prints("\n"); |
| } |
| if(c->dataqsiz > 0) |
| goto asynch; |
| |
| sgs = dequeue(&c->sendq, c); |
| if(sgs != nil) { |
| gs = sgs->g; |
| freesg(c, sgs); |
| |
| c->elemalg->copy(c->elemsize, ae, gs->elem); |
| gs->status = Grunnable; |
| return; |
| } |
| sgs = allocsg(c); |
| g->status = Gwaiting; |
| enqueue(&c->recvq, sgs); |
| sys·gosched(); |
| c->elemalg->copy(c->elemsize, ae, g->elem); |
| return; |
| |
| asynch: |
| while(c->qcount <= 0) { |
| sgs = allocsg(c); |
| g->status = Gwaiting; |
| enqueue(&c->recvq, sgs); |
| sys·gosched(); |
| } |
| c->elemalg->copy(c->elemsize, ae, c->recvdataq->elem); |
| c->recvdataq = c->recvdataq->link; |
| c->qcount--; |
| sgs = dequeue(&c->sendq, c); |
| if(gs != nil) { |
| gs = sgs->g; |
| freesg(c, sgs); |
| |
| gs->status = Grunnable; |
| } |
| } |
| |
| // chanrecv2(hchan *chan any) (elem any, pres bool); |
| void |
| sys·chanrecv2(Hchan* c, ...) |
| { |
| byte *ae, *ap; |
| SudoG *sgs; |
| G *gs; |
| |
| ae = (byte*)&c + c->eo; |
| ap = (byte*)&c + c->po; |
| |
| if(debug) { |
| prints("chanrecv2: chan="); |
| sys·printpointer(c); |
| prints("\n"); |
| } |
| if(c->dataqsiz > 0) |
| goto asynch; |
| |
| sgs = dequeue(&c->sendq, c); |
| if(sgs != nil) { |
| gs = sgs->g; |
| freesg(c, sgs); |
| |
| c->elemalg->copy(c->elemsize, ae, gs->elem); |
| gs->status = Grunnable; |
| *ap = true; |
| return; |
| } |
| *ap = false; |
| return; |
| |
| asynch: |
| if(c->qcount <= 0) { |
| *ap = false; |
| return; |
| } |
| c->elemalg->copy(c->elemsize, ae, c->recvdataq->elem); |
| c->recvdataq = c->recvdataq->link; |
| c->qcount--; |
| sgs = dequeue(&c->sendq, c); |
| if(sgs != nil) { |
| gs = sgs->g; |
| freesg(c, sgs); |
| |
| gs->status = Grunnable; |
| } |
| *ap = true; |
| } |
| |
| 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)); |
| sg->selgen = g->selgen; |
| sg->g = g; |
| return sg; |
| } |
| |
| static void |
| freesg(Hchan *c, SudoG *sg) |
| { |
| sg->link = c->free; |
| c->free = sg; |
| } |