summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/chan.goc
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/runtime/chan.goc')
-rw-r--r--src/pkg/runtime/chan.goc1155
1 files changed, 0 insertions, 1155 deletions
diff --git a/src/pkg/runtime/chan.goc b/src/pkg/runtime/chan.goc
deleted file mode 100644
index 7a584717b..000000000
--- a/src/pkg/runtime/chan.goc
+++ /dev/null
@@ -1,1155 +0,0 @@
-// 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.
-
-package runtime
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "type.h"
-#include "race.h"
-#include "malloc.h"
-#include "chan.h"
-#include "../../cmd/ld/textflag.h"
-
-uint32 runtime·Hchansize = sizeof(Hchan);
-
-static void dequeueg(WaitQ*);
-static SudoG* dequeue(WaitQ*);
-static void enqueue(WaitQ*, SudoG*);
-static void destroychan(Hchan*);
-static void racesync(Hchan*, SudoG*);
-
-static Hchan*
-makechan(ChanType *t, int64 hint)
-{
- Hchan *c;
- Type *elem;
-
- elem = t->elem;
-
- // compiler checks this but be safe.
- if(elem->size >= (1<<16))
- runtime·throw("makechan: invalid channel element type");
- if((sizeof(*c)%MAXALIGN) != 0 || elem->align > MAXALIGN)
- runtime·throw("makechan: bad alignment");
-
- if(hint < 0 || (intgo)hint != hint || (elem->size > 0 && hint > (MaxMem - sizeof(*c)) / elem->size))
- runtime·panicstring("makechan: size out of range");
-
- // allocate memory in one call
- c = (Hchan*)runtime·mallocgc(sizeof(*c) + hint*elem->size, (uintptr)t | TypeInfo_Chan, 0);
- c->elemsize = elem->size;
- c->elemtype = elem;
- c->dataqsiz = hint;
-
- if(debug)
- runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; dataqsiz=%D\n",
- c, (int64)elem->size, elem->alg, (int64)c->dataqsiz);
-
- return c;
-}
-
-func reflect·makechan(t *ChanType, size uint64) (c *Hchan) {
- c = makechan(t, size);
-}
-
-func makechan(t *ChanType, size int64) (c *Hchan) {
- c = makechan(t, size);
-}
-
-/*
- * 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.
- *
- * sleep can wake up with g->param == nil
- * when a channel involved in the sleep has
- * been closed. it is easiest to loop and re-run
- * the operation; we'll see that it's now closed.
- */
-static bool
-chansend(ChanType *t, Hchan *c, byte *ep, bool block, void *pc)
-{
- SudoG *sg;
- SudoG mysg;
- G* gp;
- int64 t0;
-
- if(raceenabled)
- runtime·racereadobjectpc(ep, t->elem, runtime·getcallerpc(&t), chansend);
-
- if(c == nil) {
- USED(t);
- if(!block)
- return false;
- runtime·park(nil, nil, "chan send (nil chan)");
- return false; // not reached
- }
-
- if(debug) {
- runtime·printf("chansend: chan=%p; elem=", c);
- c->elemtype->alg->print(c->elemsize, ep);
- runtime·prints("\n");
- }
-
- t0 = 0;
- mysg.releasetime = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- mysg.releasetime = -1;
- }
-
- runtime·lock(c);
- if(raceenabled)
- runtime·racereadpc(c, pc, chansend);
- if(c->closed)
- goto closed;
-
- if(c->dataqsiz > 0)
- goto asynch;
-
- sg = dequeue(&c->recvq);
- if(sg != nil) {
- if(raceenabled)
- racesync(c, sg);
- runtime·unlock(c);
-
- gp = sg->g;
- gp->param = sg;
- if(sg->elem != nil)
- c->elemtype->alg->copy(c->elemsize, sg->elem, ep);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- return true;
- }
-
- if(!block) {
- runtime·unlock(c);
- return false;
- }
-
- mysg.elem = ep;
- mysg.g = g;
- mysg.selectdone = nil;
- g->param = nil;
- enqueue(&c->sendq, &mysg);
- runtime·parkunlock(c, "chan send");
-
- if(g->param == nil) {
- runtime·lock(c);
- if(!c->closed)
- runtime·throw("chansend: spurious wakeup");
- goto closed;
- }
-
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
-
- return true;
-
-asynch:
- if(c->closed)
- goto closed;
-
- if(c->qcount >= c->dataqsiz) {
- if(!block) {
- runtime·unlock(c);
- return false;
- }
- mysg.g = g;
- mysg.elem = nil;
- mysg.selectdone = nil;
- enqueue(&c->sendq, &mysg);
- runtime·parkunlock(c, "chan send");
-
- runtime·lock(c);
- goto asynch;
- }
-
- if(raceenabled) {
- runtime·raceacquire(chanbuf(c, c->sendx));
- runtime·racerelease(chanbuf(c, c->sendx));
- }
-
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
- if(++c->sendx == c->dataqsiz)
- c->sendx = 0;
- c->qcount++;
-
- sg = dequeue(&c->recvq);
- if(sg != nil) {
- gp = sg->g;
- runtime·unlock(c);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else
- runtime·unlock(c);
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-
-closed:
- runtime·unlock(c);
- runtime·panicstring("send on closed channel");
- return false; // not reached
-}
-
-
-static bool
-chanrecv(ChanType *t, Hchan* c, byte *ep, bool block, bool *received)
-{
- SudoG *sg;
- SudoG mysg;
- G *gp;
- int64 t0;
-
- // raceenabled: don't need to check ep, as it is always on the stack.
-
- if(debug)
- runtime·printf("chanrecv: chan=%p\n", c);
-
- if(c == nil) {
- USED(t);
- if(!block)
- return false;
- runtime·park(nil, nil, "chan receive (nil chan)");
- return false; // not reached
- }
-
- t0 = 0;
- mysg.releasetime = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- mysg.releasetime = -1;
- }
-
- runtime·lock(c);
- if(c->dataqsiz > 0)
- goto asynch;
-
- if(c->closed)
- goto closed;
-
- sg = dequeue(&c->sendq);
- if(sg != nil) {
- if(raceenabled)
- racesync(c, sg);
- runtime·unlock(c);
-
- if(ep != nil)
- c->elemtype->alg->copy(c->elemsize, ep, sg->elem);
- gp = sg->g;
- gp->param = sg;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
-
- if(received != nil)
- *received = true;
- return true;
- }
-
- if(!block) {
- runtime·unlock(c);
- return false;
- }
-
- mysg.elem = ep;
- mysg.g = g;
- mysg.selectdone = nil;
- g->param = nil;
- enqueue(&c->recvq, &mysg);
- runtime·parkunlock(c, "chan receive");
-
- if(g->param == nil) {
- runtime·lock(c);
- if(!c->closed)
- runtime·throw("chanrecv: spurious wakeup");
- goto closed;
- }
-
- if(received != nil)
- *received = true;
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-
-asynch:
- if(c->qcount <= 0) {
- if(c->closed)
- goto closed;
-
- if(!block) {
- runtime·unlock(c);
- if(received != nil)
- *received = false;
- return false;
- }
- mysg.g = g;
- mysg.elem = nil;
- mysg.selectdone = nil;
- enqueue(&c->recvq, &mysg);
- runtime·parkunlock(c, "chan receive");
-
- runtime·lock(c);
- goto asynch;
- }
-
- if(raceenabled) {
- runtime·raceacquire(chanbuf(c, c->recvx));
- runtime·racerelease(chanbuf(c, c->recvx));
- }
-
- if(ep != nil)
- c->elemtype->alg->copy(c->elemsize, ep, chanbuf(c, c->recvx));
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
- if(++c->recvx == c->dataqsiz)
- c->recvx = 0;
- c->qcount--;
-
- sg = dequeue(&c->sendq);
- if(sg != nil) {
- gp = sg->g;
- runtime·unlock(c);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else
- runtime·unlock(c);
-
- if(received != nil)
- *received = true;
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-
-closed:
- if(ep != nil)
- c->elemtype->alg->copy(c->elemsize, ep, nil);
- if(received != nil)
- *received = false;
- if(raceenabled)
- runtime·raceacquire(c);
- runtime·unlock(c);
- if(mysg.releasetime > 0)
- runtime·blockevent(mysg.releasetime - t0, 2);
- return true;
-}
-
-#pragma textflag NOSPLIT
-func chansend1(t *ChanType, c *Hchan, elem *byte) {
- chansend(t, c, elem, true, runtime·getcallerpc(&t));
-}
-
-#pragma textflag NOSPLIT
-func chanrecv1(t *ChanType, c *Hchan, elem *byte) {
- chanrecv(t, c, elem, true, nil);
-}
-
-// chanrecv2(hchan *chan any, elem *any) (received bool);
-#pragma textflag NOSPLIT
-func chanrecv2(t *ChanType, c *Hchan, elem *byte) (received bool) {
- chanrecv(t, c, elem, true, &received);
-}
-
-// compiler implements
-//
-// select {
-// case c <- v:
-// ... foo
-// default:
-// ... bar
-// }
-//
-// as
-//
-// if selectnbsend(c, v) {
-// ... foo
-// } else {
-// ... bar
-// }
-//
-#pragma textflag NOSPLIT
-func selectnbsend(t *ChanType, c *Hchan, elem *byte) (selected bool) {
- selected = chansend(t, c, elem, false, runtime·getcallerpc(&t));
-}
-
-// compiler implements
-//
-// select {
-// case v = <-c:
-// ... foo
-// default:
-// ... bar
-// }
-//
-// as
-//
-// if selectnbrecv(&v, c) {
-// ... foo
-// } else {
-// ... bar
-// }
-//
-#pragma textflag NOSPLIT
-func selectnbrecv(t *ChanType, elem *byte, c *Hchan) (selected bool) {
- selected = chanrecv(t, c, elem, false, nil);
-}
-
-// compiler implements
-//
-// select {
-// case v, ok = <-c:
-// ... foo
-// default:
-// ... bar
-// }
-//
-// as
-//
-// if c != nil && selectnbrecv2(&v, &ok, c) {
-// ... foo
-// } else {
-// ... bar
-// }
-//
-#pragma textflag NOSPLIT
-func selectnbrecv2(t *ChanType, elem *byte, received *bool, c *Hchan) (selected bool) {
- selected = chanrecv(t, c, elem, false, received);
-}
-
-#pragma textflag NOSPLIT
-func reflect·chansend(t *ChanType, c *Hchan, elem *byte, nb bool) (selected bool) {
- selected = chansend(t, c, elem, !nb, runtime·getcallerpc(&t));
-}
-
-func reflect·chanrecv(t *ChanType, c *Hchan, nb bool, elem *byte) (selected bool, received bool) {
- received = false;
- selected = chanrecv(t, c, elem, !nb, &received);
-}
-
-static Select* newselect(int32);
-
-#pragma textflag NOSPLIT
-func newselect(size int32) (sel *byte) {
- sel = (byte*)newselect(size);
-}
-
-static Select*
-newselect(int32 size)
-{
- int32 n;
- Select *sel;
-
- n = 0;
- if(size > 1)
- n = size-1;
-
- // allocate all the memory we need in a single allocation
- // start with Select with size cases
- // then lockorder with size entries
- // then pollorder with size entries
- sel = runtime·mal(sizeof(*sel) +
- n*sizeof(sel->scase[0]) +
- size*sizeof(sel->lockorder[0]) +
- size*sizeof(sel->pollorder[0]));
-
- sel->tcase = size;
- sel->ncase = 0;
- sel->lockorder = (void*)(sel->scase + size);
- sel->pollorder = (void*)(sel->lockorder + size);
-
- if(debug)
- runtime·printf("newselect s=%p size=%d\n", sel, size);
- return sel;
-}
-
-// cut in half to give stack a chance to split
-static void selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so);
-
-#pragma textflag NOSPLIT
-func selectsend(sel *Select, c *Hchan, elem *byte) (selected bool) {
- selected = false;
-
- // nil cases do not compete
- if(c != nil)
- selectsend(sel, c, runtime·getcallerpc(&sel), elem, (byte*)&selected - (byte*)&sel);
-}
-
-static void
-selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so)
-{
- int32 i;
- Scase *cas;
-
- i = sel->ncase;
- if(i >= sel->tcase)
- runtime·throw("selectsend: too many cases");
- sel->ncase = i+1;
- cas = &sel->scase[i];
-
- cas->pc = pc;
- cas->chan = c;
- cas->so = so;
- cas->kind = CaseSend;
- cas->sg.elem = elem;
-
- if(debug)
- runtime·printf("selectsend s=%p pc=%p chan=%p so=%d\n",
- sel, cas->pc, cas->chan, cas->so);
-}
-
-// cut in half to give stack a chance to split
-static void selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool*, int32 so);
-
-#pragma textflag NOSPLIT
-func selectrecv(sel *Select, c *Hchan, elem *byte) (selected bool) {
- selected = false;
-
- // nil cases do not compete
- if(c != nil)
- selectrecv(sel, c, runtime·getcallerpc(&sel), elem, nil, (byte*)&selected - (byte*)&sel);
-}
-
-#pragma textflag NOSPLIT
-func selectrecv2(sel *Select, c *Hchan, elem *byte, received *bool) (selected bool) {
- selected = false;
-
- // nil cases do not compete
- if(c != nil)
- selectrecv(sel, c, runtime·getcallerpc(&sel), elem, received, (byte*)&selected - (byte*)&sel);
-}
-
-static void
-selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool *received, int32 so)
-{
- int32 i;
- Scase *cas;
-
- i = sel->ncase;
- if(i >= sel->tcase)
- runtime·throw("selectrecv: too many cases");
- sel->ncase = i+1;
- cas = &sel->scase[i];
- cas->pc = pc;
- cas->chan = c;
-
- cas->so = so;
- cas->kind = CaseRecv;
- cas->sg.elem = elem;
- cas->receivedp = received;
-
- if(debug)
- runtime·printf("selectrecv s=%p pc=%p chan=%p so=%d\n",
- sel, cas->pc, cas->chan, cas->so);
-}
-
-// cut in half to give stack a chance to split
-static void selectdefault(Select*, void*, int32);
-
-#pragma textflag NOSPLIT
-func selectdefault(sel *Select) (selected bool) {
- selected = false;
- selectdefault(sel, runtime·getcallerpc(&sel), (byte*)&selected - (byte*)&sel);
-}
-
-static void
-selectdefault(Select *sel, void *callerpc, int32 so)
-{
- int32 i;
- Scase *cas;
-
- i = sel->ncase;
- if(i >= sel->tcase)
- runtime·throw("selectdefault: too many cases");
- sel->ncase = i+1;
- cas = &sel->scase[i];
- cas->pc = callerpc;
- cas->chan = nil;
-
- cas->so = so;
- cas->kind = CaseDefault;
-
- if(debug)
- runtime·printf("selectdefault s=%p pc=%p so=%d\n",
- sel, cas->pc, cas->so);
-}
-
-static void
-sellock(Select *sel)
-{
- uint32 i;
- Hchan *c, *c0;
-
- c = nil;
- for(i=0; i<sel->ncase; i++) {
- c0 = sel->lockorder[i];
- if(c0 && c0 != c) {
- c = sel->lockorder[i];
- runtime·lock(c);
- }
- }
-}
-
-static void
-selunlock(Select *sel)
-{
- int32 i, n, r;
- Hchan *c;
-
- // We must be very careful here to not touch sel after we have unlocked
- // the last lock, because sel can be freed right after the last unlock.
- // Consider the following situation.
- // First M calls runtime·park() in runtime·selectgo() passing the sel.
- // Once runtime·park() has unlocked the last lock, another M makes
- // the G that calls select runnable again and schedules it for execution.
- // When the G runs on another M, it locks all the locks and frees sel.
- // Now if the first M touches sel, it will access freed memory.
- n = (int32)sel->ncase;
- r = 0;
- // skip the default case
- if(n>0 && sel->lockorder[0] == nil)
- r = 1;
- for(i = n-1; i >= r; i--) {
- c = sel->lockorder[i];
- if(i>0 && sel->lockorder[i-1] == c)
- continue; // will unlock it on the next iteration
- runtime·unlock(c);
- }
-}
-
-static bool
-selparkcommit(G *gp, void *sel)
-{
- USED(gp);
- selunlock(sel);
- return true;
-}
-
-func block() {
- runtime·park(nil, nil, "select (no cases)"); // forever
-}
-
-static void* selectgo(Select**);
-
-// selectgo(sel *byte);
-//
-// overwrites return pc on stack to signal which case of the select
-// to run, so cannot appear at the top of a split stack.
-#pragma textflag NOSPLIT
-func selectgo(sel *Select) {
- runtime·setcallerpc(&sel, selectgo(&sel));
-}
-
-static void*
-selectgo(Select **selp)
-{
- Select *sel;
- uint32 o, i, j, k, done;
- int64 t0;
- Scase *cas, *dfl;
- Hchan *c;
- SudoG *sg;
- G *gp;
- byte *as;
- void *pc;
-
- sel = *selp;
-
- if(debug)
- runtime·printf("select: sel=%p\n", sel);
-
- t0 = 0;
- if(runtime·blockprofilerate > 0) {
- t0 = runtime·cputicks();
- for(i=0; i<sel->ncase; i++)
- sel->scase[i].sg.releasetime = -1;
- }
-
- // The compiler rewrites selects that statically have
- // only 0 or 1 cases plus default into simpler constructs.
- // The only way we can end up with such small sel->ncase
- // values here is for a larger select in which most channels
- // have been nilled out. The general code handles those
- // cases correctly, and they are rare enough not to bother
- // optimizing (and needing to test).
-
- // generate permuted order
- for(i=0; i<sel->ncase; i++)
- sel->pollorder[i] = i;
- for(i=1; i<sel->ncase; i++) {
- o = sel->pollorder[i];
- j = runtime·fastrand1()%(i+1);
- sel->pollorder[i] = sel->pollorder[j];
- sel->pollorder[j] = o;
- }
-
- // sort the cases by Hchan address to get the locking order.
- // simple heap sort, to guarantee n log n time and constant stack footprint.
- for(i=0; i<sel->ncase; i++) {
- j = i;
- c = sel->scase[j].chan;
- while(j > 0 && sel->lockorder[k=(j-1)/2] < c) {
- sel->lockorder[j] = sel->lockorder[k];
- j = k;
- }
- sel->lockorder[j] = c;
- }
- for(i=sel->ncase; i-->0; ) {
- c = sel->lockorder[i];
- sel->lockorder[i] = sel->lockorder[0];
- j = 0;
- for(;;) {
- k = j*2+1;
- if(k >= i)
- break;
- if(k+1 < i && sel->lockorder[k] < sel->lockorder[k+1])
- k++;
- if(c < sel->lockorder[k]) {
- sel->lockorder[j] = sel->lockorder[k];
- j = k;
- continue;
- }
- break;
- }
- sel->lockorder[j] = c;
- }
- /*
- for(i=0; i+1<sel->ncase; i++)
- if(sel->lockorder[i] > sel->lockorder[i+1]) {
- runtime·printf("i=%d %p %p\n", i, sel->lockorder[i], sel->lockorder[i+1]);
- runtime·throw("select: broken sort");
- }
- */
- sellock(sel);
-
-loop:
- // pass 1 - look for something already waiting
- dfl = nil;
- for(i=0; i<sel->ncase; i++) {
- o = sel->pollorder[i];
- cas = &sel->scase[o];
- c = cas->chan;
-
- switch(cas->kind) {
- case CaseRecv:
- if(c->dataqsiz > 0) {
- if(c->qcount > 0)
- goto asyncrecv;
- } else {
- sg = dequeue(&c->sendq);
- if(sg != nil)
- goto syncrecv;
- }
- if(c->closed)
- goto rclose;
- break;
-
- case CaseSend:
- if(raceenabled)
- runtime·racereadpc(c, cas->pc, chansend);
- if(c->closed)
- goto sclose;
- if(c->dataqsiz > 0) {
- if(c->qcount < c->dataqsiz)
- goto asyncsend;
- } else {
- sg = dequeue(&c->recvq);
- if(sg != nil)
- goto syncsend;
- }
- break;
-
- case CaseDefault:
- dfl = cas;
- break;
- }
- }
-
- if(dfl != nil) {
- selunlock(sel);
- cas = dfl;
- goto retc;
- }
-
-
- // pass 2 - enqueue on all chans
- done = 0;
- for(i=0; i<sel->ncase; i++) {
- o = sel->pollorder[i];
- cas = &sel->scase[o];
- c = cas->chan;
- sg = &cas->sg;
- sg->g = g;
- sg->selectdone = &done;
-
- switch(cas->kind) {
- case CaseRecv:
- enqueue(&c->recvq, sg);
- break;
-
- case CaseSend:
- enqueue(&c->sendq, sg);
- break;
- }
- }
-
- g->param = nil;
- runtime·park(selparkcommit, sel, "select");
-
- sellock(sel);
- sg = g->param;
-
- // pass 3 - dequeue from unsuccessful chans
- // otherwise they stack up on quiet channels
- for(i=0; i<sel->ncase; i++) {
- cas = &sel->scase[i];
- if(cas != (Scase*)sg) {
- c = cas->chan;
- if(cas->kind == CaseSend)
- dequeueg(&c->sendq);
- else
- dequeueg(&c->recvq);
- }
- }
-
- if(sg == nil)
- goto loop;
-
- cas = (Scase*)sg;
- c = cas->chan;
-
- if(c->dataqsiz > 0)
- runtime·throw("selectgo: shouldn't happen");
-
- if(debug)
- runtime·printf("wait-return: sel=%p c=%p cas=%p kind=%d\n",
- sel, c, cas, cas->kind);
-
- if(cas->kind == CaseRecv) {
- if(cas->receivedp != nil)
- *cas->receivedp = true;
- }
-
- if(raceenabled) {
- if(cas->kind == CaseRecv && cas->sg.elem != nil)
- runtime·racewriteobjectpc(cas->sg.elem, c->elemtype, cas->pc, chanrecv);
- else if(cas->kind == CaseSend)
- runtime·racereadobjectpc(cas->sg.elem, c->elemtype, cas->pc, chansend);
- }
-
- selunlock(sel);
- goto retc;
-
-asyncrecv:
- // can receive from buffer
- if(raceenabled) {
- if(cas->sg.elem != nil)
- runtime·racewriteobjectpc(cas->sg.elem, c->elemtype, cas->pc, chanrecv);
- runtime·raceacquire(chanbuf(c, c->recvx));
- runtime·racerelease(chanbuf(c, c->recvx));
- }
- if(cas->receivedp != nil)
- *cas->receivedp = true;
- if(cas->sg.elem != nil)
- c->elemtype->alg->copy(c->elemsize, cas->sg.elem, chanbuf(c, c->recvx));
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
- if(++c->recvx == c->dataqsiz)
- c->recvx = 0;
- c->qcount--;
- sg = dequeue(&c->sendq);
- if(sg != nil) {
- gp = sg->g;
- selunlock(sel);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else {
- selunlock(sel);
- }
- goto retc;
-
-asyncsend:
- // can send to buffer
- if(raceenabled) {
- runtime·raceacquire(chanbuf(c, c->sendx));
- runtime·racerelease(chanbuf(c, c->sendx));
- runtime·racereadobjectpc(cas->sg.elem, c->elemtype, cas->pc, chansend);
- }
- c->elemtype->alg->copy(c->elemsize, chanbuf(c, c->sendx), cas->sg.elem);
- if(++c->sendx == c->dataqsiz)
- c->sendx = 0;
- c->qcount++;
- sg = dequeue(&c->recvq);
- if(sg != nil) {
- gp = sg->g;
- selunlock(sel);
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- } else {
- selunlock(sel);
- }
- goto retc;
-
-syncrecv:
- // can receive from sleeping sender (sg)
- if(raceenabled) {
- if(cas->sg.elem != nil)
- runtime·racewriteobjectpc(cas->sg.elem, c->elemtype, cas->pc, chanrecv);
- racesync(c, sg);
- }
- selunlock(sel);
- if(debug)
- runtime·printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
- if(cas->receivedp != nil)
- *cas->receivedp = true;
- if(cas->sg.elem != nil)
- c->elemtype->alg->copy(c->elemsize, cas->sg.elem, sg->elem);
- gp = sg->g;
- gp->param = sg;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- goto retc;
-
-rclose:
- // read at end of closed channel
- selunlock(sel);
- if(cas->receivedp != nil)
- *cas->receivedp = false;
- if(cas->sg.elem != nil)
- c->elemtype->alg->copy(c->elemsize, cas->sg.elem, nil);
- if(raceenabled)
- runtime·raceacquire(c);
- goto retc;
-
-syncsend:
- // can send to sleeping receiver (sg)
- if(raceenabled) {
- runtime·racereadobjectpc(cas->sg.elem, c->elemtype, cas->pc, chansend);
- racesync(c, sg);
- }
- selunlock(sel);
- if(debug)
- runtime·printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
- if(sg->elem != nil)
- c->elemtype->alg->copy(c->elemsize, sg->elem, cas->sg.elem);
- gp = sg->g;
- gp->param = sg;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
-
-retc:
- // return pc corresponding to chosen case.
- // Set boolean passed during select creation
- // (at offset selp + cas->so) to true.
- // If cas->so == 0, this is a reflect-driven select and we
- // don't need to update the boolean.
- pc = cas->pc;
- if(cas->so > 0) {
- as = (byte*)selp + cas->so;
- *as = true;
- }
- if(cas->sg.releasetime > 0)
- runtime·blockevent(cas->sg.releasetime - t0, 2);
- runtime·free(sel);
- return pc;
-
-sclose:
- // send on closed channel
- selunlock(sel);
- runtime·panicstring("send on closed channel");
- return nil; // not reached
-}
-
-// This struct must match ../reflect/value.go:/runtimeSelect.
-typedef struct runtimeSelect runtimeSelect;
-struct runtimeSelect
-{
- uintptr dir;
- ChanType *typ;
- Hchan *ch;
- byte *val;
-};
-
-// This enum must match ../reflect/value.go:/SelectDir.
-enum SelectDir {
- SelectSend = 1,
- SelectRecv,
- SelectDefault,
-};
-
-func reflect·rselect(cases Slice) (chosen int, recvOK bool) {
- int32 i;
- Select *sel;
- runtimeSelect* rcase, *rc;
-
- chosen = -1;
- recvOK = false;
-
- rcase = (runtimeSelect*)cases.array;
-
- sel = newselect(cases.len);
- for(i=0; i<cases.len; i++) {
- rc = &rcase[i];
- switch(rc->dir) {
- case SelectDefault:
- selectdefault(sel, (void*)i, 0);
- break;
- case SelectSend:
- if(rc->ch == nil)
- break;
- selectsend(sel, rc->ch, (void*)i, rc->val, 0);
- break;
- case SelectRecv:
- if(rc->ch == nil)
- break;
- selectrecv(sel, rc->ch, (void*)i, rc->val, &recvOK, 0);
- break;
- }
- }
-
- chosen = (intgo)(uintptr)selectgo(&sel);
-}
-
-static void closechan(Hchan *c, void *pc);
-
-#pragma textflag NOSPLIT
-func closechan(c *Hchan) {
- closechan(c, runtime·getcallerpc(&c));
-}
-
-#pragma textflag NOSPLIT
-func reflect·chanclose(c *Hchan) {
- closechan(c, runtime·getcallerpc(&c));
-}
-
-static void
-closechan(Hchan *c, void *pc)
-{
- SudoG *sg;
- G* gp;
-
- if(c == nil)
- runtime·panicstring("close of nil channel");
-
- runtime·lock(c);
- if(c->closed) {
- runtime·unlock(c);
- runtime·panicstring("close of closed channel");
- }
-
- if(raceenabled) {
- runtime·racewritepc(c, pc, runtime·closechan);
- runtime·racerelease(c);
- }
-
- c->closed = true;
-
- // release all readers
- for(;;) {
- sg = dequeue(&c->recvq);
- if(sg == nil)
- break;
- gp = sg->g;
- gp->param = nil;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- }
-
- // release all writers
- for(;;) {
- sg = dequeue(&c->sendq);
- if(sg == nil)
- break;
- gp = sg->g;
- gp->param = nil;
- if(sg->releasetime)
- sg->releasetime = runtime·cputicks();
- runtime·ready(gp);
- }
-
- runtime·unlock(c);
-}
-
-func reflect·chanlen(c *Hchan) (len int) {
- if(c == nil)
- len = 0;
- else
- len = c->qcount;
-}
-
-func reflect·chancap(c *Hchan) (cap int) {
- if(c == nil)
- cap = 0;
- else
- cap = c->dataqsiz;
-}
-
-static SudoG*
-dequeue(WaitQ *q)
-{
- SudoG *sgp;
-
-loop:
- sgp = q->first;
- if(sgp == nil)
- return nil;
- q->first = sgp->link;
-
- // if sgp participates in a select and is already signaled, ignore it
- if(sgp->selectdone != nil) {
- // claim the right to signal
- if(*sgp->selectdone != 0 || !runtime·cas(sgp->selectdone, 0, 1))
- goto loop;
- }
-
- return sgp;
-}
-
-static void
-dequeueg(WaitQ *q)
-{
- SudoG **l, *sgp, *prevsgp;
-
- prevsgp = nil;
- for(l=&q->first; (sgp=*l) != nil; l=&sgp->link, prevsgp=sgp) {
- if(sgp->g == g) {
- *l = sgp->link;
- if(q->last == sgp)
- q->last = prevsgp;
- break;
- }
- }
-}
-
-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 void
-racesync(Hchan *c, SudoG *sg)
-{
- runtime·racerelease(chanbuf(c, 0));
- runtime·raceacquireg(sg->g, chanbuf(c, 0));
- runtime·racereleaseg(sg->g, chanbuf(c, 0));
- runtime·raceacquire(chanbuf(c, 0));
-}