aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.7/libgo/runtime/chan.c
diff options
context:
space:
mode:
authorBen Cheng <bccheng@google.com>2012-10-01 10:30:31 -0700
committerBen Cheng <bccheng@google.com>2012-10-01 10:30:31 -0700
commit82bcbebce43f0227f506d75a5b764b6847041bae (patch)
treefe9f8597b48a430c4daeb5123e3e8eb28e6f9da9 /gcc-4.7/libgo/runtime/chan.c
parent3c052de3bb16ac53b6b6ed659ec7557eb84c7590 (diff)
downloadtoolchain_gcc-82bcbebce43f0227f506d75a5b764b6847041bae.tar.gz
toolchain_gcc-82bcbebce43f0227f506d75a5b764b6847041bae.tar.bz2
toolchain_gcc-82bcbebce43f0227f506d75a5b764b6847041bae.zip
Initial check-in of gcc 4.7.2.
Change-Id: I4a2f5a921c21741a0e18bda986d77e5f1bef0365
Diffstat (limited to 'gcc-4.7/libgo/runtime/chan.c')
-rw-r--r--gcc-4.7/libgo/runtime/chan.c1273
1 files changed, 1273 insertions, 0 deletions
diff --git a/gcc-4.7/libgo/runtime/chan.c b/gcc-4.7/libgo/runtime/chan.c
new file mode 100644
index 000000000..c8ee10e8c
--- /dev/null
+++ b/gcc-4.7/libgo/runtime/chan.c
@@ -0,0 +1,1273 @@
+// 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"
+#include "go-type.h"
+
+#define NOSELGEN 1
+
+static int32 debug = 0;
+
+typedef struct WaitQ WaitQ;
+typedef struct SudoG SudoG;
+typedef struct Select Select;
+typedef struct Scase Scase;
+
+typedef struct __go_type_descriptor Type;
+typedef struct __go_channel_type ChanType;
+
+struct SudoG
+{
+ G* g; // g and selgen constitute
+ uint32 selgen; // a weak pointer to g
+ SudoG* link;
+ byte* elem; // data element
+};
+
+struct WaitQ
+{
+ SudoG* first;
+ SudoG* last;
+};
+
+struct Hchan
+{
+ uint32 qcount; // total data in the q
+ uint32 dataqsiz; // size of the circular q
+ uint16 elemsize;
+ bool closed;
+ uint8 elemalign;
+ uint32 sendx; // send index
+ uint32 recvx; // receive index
+ WaitQ recvq; // list of recv waiters
+ WaitQ sendq; // list of send waiters
+ Lock;
+};
+
+// Buffer follows Hchan immediately in memory.
+// chanbuf(c, i) is pointer to the i'th slot in the buffer.
+#define chanbuf(c, i) ((byte*)((c)+1)+(uintptr)(c)->elemsize*(i))
+
+enum
+{
+ // Scase.kind
+ CaseRecv,
+ CaseSend,
+ CaseDefault,
+};
+
+struct Scase
+{
+ SudoG sg; // must be first member (cast to Scase)
+ Hchan* chan; // chan
+ uint16 kind;
+ uint16 index; // index to return
+ bool* receivedp; // pointer to received bool (recv2)
+};
+
+struct Select
+{
+ uint16 tcase; // total count of scase[]
+ uint16 ncase; // currently filled scase[]
+ uint16* pollorder; // case poll order
+ Hchan** lockorder; // channel lock order
+ Scase scase[1]; // one per case (in order of appearance)
+};
+
+static void dequeueg(WaitQ*);
+static SudoG* dequeue(WaitQ*);
+static void enqueue(WaitQ*, SudoG*);
+
+Hchan*
+runtime_makechan_c(ChanType *t, int64 hint)
+{
+ Hchan *c;
+ int32 n;
+ const Type *elem;
+
+ elem = t->__element_type;
+
+ if(hint < 0 || (int32)hint != hint || (elem->__size > 0 && (uintptr)hint > ((uintptr)-1) / elem->__size))
+ runtime_panicstring("makechan: size out of range");
+
+ n = sizeof(*c);
+
+ // allocate memory in one call
+ c = (Hchan*)runtime_mal(n + hint*elem->__size);
+ c->elemsize = elem->__size;
+ c->elemalign = elem->__align;
+ c->dataqsiz = hint;
+
+ if(debug)
+ runtime_printf("makechan: chan=%p; elemsize=%D; elemalign=%d; dataqsiz=%d\n",
+ c, (int64)elem->__size, elem->__align, c->dataqsiz);
+
+ return c;
+}
+
+// For reflect
+// func makechan(typ *ChanType, size uint32) (chan)
+uintptr reflect_makechan(ChanType *, uint32)
+ asm ("reflect.makechan");
+
+uintptr
+reflect_makechan(ChanType *t, uint32 size)
+{
+ void *ret;
+ Hchan *c;
+
+ c = runtime_makechan_c(t, size);
+ ret = runtime_mal(sizeof(void*));
+ __builtin_memcpy(ret, &c, sizeof(void*));
+ return (uintptr)ret;
+}
+
+// makechan(t *ChanType, hint int64) (hchan *chan any);
+Hchan*
+__go_new_channel(ChanType *t, uintptr hint)
+{
+ return runtime_makechan_c(t, hint);
+}
+
+Hchan*
+__go_new_channel_big(ChanType *t, uint64 hint)
+{
+ return runtime_makechan_c(t, hint);
+}
+
+/*
+ * 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.
+ */
+void
+runtime_chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
+{
+ SudoG *sg;
+ SudoG mysg;
+ G* gp;
+ G* g;
+
+ g = runtime_g();
+
+ if(c == nil) {
+ USED(t);
+ if(pres != nil) {
+ *pres = false;
+ return;
+ }
+ g->status = Gwaiting;
+ g->waitreason = "chan send (nil chan)";
+ runtime_gosched();
+ return; // not reached
+ }
+
+ if(runtime_gcwaiting)
+ runtime_gosched();
+
+ if(debug) {
+ runtime_printf("chansend: chan=%p\n", c);
+ }
+
+ runtime_lock(c);
+ if(c->closed)
+ goto closed;
+
+ if(c->dataqsiz > 0)
+ goto asynch;
+
+ sg = dequeue(&c->recvq);
+ if(sg != nil) {
+ runtime_unlock(c);
+
+ gp = sg->g;
+ gp->param = sg;
+ if(sg->elem != nil)
+ runtime_memmove(sg->elem, ep, c->elemsize);
+ runtime_ready(gp);
+
+ if(pres != nil)
+ *pres = true;
+ return;
+ }
+
+ if(pres != nil) {
+ runtime_unlock(c);
+ *pres = false;
+ return;
+ }
+
+ mysg.elem = ep;
+ mysg.g = g;
+ mysg.selgen = NOSELGEN;
+ g->param = nil;
+ g->status = Gwaiting;
+ g->waitreason = "chan send";
+ enqueue(&c->sendq, &mysg);
+ runtime_unlock(c);
+ runtime_gosched();
+
+ if(g->param == nil) {
+ runtime_lock(c);
+ if(!c->closed)
+ runtime_throw("chansend: spurious wakeup");
+ goto closed;
+ }
+
+ return;
+
+asynch:
+ if(c->closed)
+ goto closed;
+
+ if(c->qcount >= c->dataqsiz) {
+ if(pres != nil) {
+ runtime_unlock(c);
+ *pres = false;
+ return;
+ }
+ mysg.g = g;
+ mysg.elem = nil;
+ mysg.selgen = NOSELGEN;
+ g->status = Gwaiting;
+ g->waitreason = "chan send";
+ enqueue(&c->sendq, &mysg);
+ runtime_unlock(c);
+ runtime_gosched();
+
+ runtime_lock(c);
+ goto asynch;
+ }
+ runtime_memmove(chanbuf(c, c->sendx), ep, c->elemsize);
+ if(++c->sendx == c->dataqsiz)
+ c->sendx = 0;
+ c->qcount++;
+
+ sg = dequeue(&c->recvq);
+ if(sg != nil) {
+ gp = sg->g;
+ runtime_unlock(c);
+ runtime_ready(gp);
+ } else
+ runtime_unlock(c);
+ if(pres != nil)
+ *pres = true;
+ return;
+
+closed:
+ runtime_unlock(c);
+ runtime_panicstring("send on closed channel");
+}
+
+
+void
+runtime_chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *received)
+{
+ SudoG *sg;
+ SudoG mysg;
+ G *gp;
+ G *g;
+
+ if(runtime_gcwaiting)
+ runtime_gosched();
+
+ if(debug)
+ runtime_printf("chanrecv: chan=%p\n", c);
+
+ g = runtime_g();
+
+ if(c == nil) {
+ USED(t);
+ if(selected != nil) {
+ *selected = false;
+ return;
+ }
+ g->status = Gwaiting;
+ g->waitreason = "chan receive (nil chan)";
+ runtime_gosched();
+ return; // not reached
+ }
+
+ runtime_lock(c);
+ if(c->dataqsiz > 0)
+ goto asynch;
+
+ if(c->closed)
+ goto closed;
+
+ sg = dequeue(&c->sendq);
+ if(sg != nil) {
+ runtime_unlock(c);
+
+ if(ep != nil)
+ runtime_memmove(ep, sg->elem, c->elemsize);
+ gp = sg->g;
+ gp->param = sg;
+ runtime_ready(gp);
+
+ if(selected != nil)
+ *selected = true;
+ if(received != nil)
+ *received = true;
+ return;
+ }
+
+ if(selected != nil) {
+ runtime_unlock(c);
+ *selected = false;
+ return;
+ }
+
+ mysg.elem = ep;
+ mysg.g = g;
+ mysg.selgen = NOSELGEN;
+ g->param = nil;
+ g->status = Gwaiting;
+ g->waitreason = "chan receive";
+ enqueue(&c->recvq, &mysg);
+ runtime_unlock(c);
+ runtime_gosched();
+
+ if(g->param == nil) {
+ runtime_lock(c);
+ if(!c->closed)
+ runtime_throw("chanrecv: spurious wakeup");
+ goto closed;
+ }
+
+ if(received != nil)
+ *received = true;
+ return;
+
+asynch:
+ if(c->qcount <= 0) {
+ if(c->closed)
+ goto closed;
+
+ if(selected != nil) {
+ runtime_unlock(c);
+ *selected = false;
+ if(received != nil)
+ *received = false;
+ return;
+ }
+ mysg.g = g;
+ mysg.elem = nil;
+ mysg.selgen = NOSELGEN;
+ g->status = Gwaiting;
+ g->waitreason = "chan receive";
+ enqueue(&c->recvq, &mysg);
+ runtime_unlock(c);
+ runtime_gosched();
+
+ runtime_lock(c);
+ goto asynch;
+ }
+ if(ep != nil)
+ runtime_memmove(ep, chanbuf(c, c->recvx), c->elemsize);
+ runtime_memclr(chanbuf(c, c->recvx), c->elemsize);
+ if(++c->recvx == c->dataqsiz)
+ c->recvx = 0;
+ c->qcount--;
+
+ sg = dequeue(&c->sendq);
+ if(sg != nil) {
+ gp = sg->g;
+ runtime_unlock(c);
+ runtime_ready(gp);
+ } else
+ runtime_unlock(c);
+
+ if(selected != nil)
+ *selected = true;
+ if(received != nil)
+ *received = true;
+ return;
+
+closed:
+ if(ep != nil)
+ runtime_memclr(ep, c->elemsize);
+ if(selected != nil)
+ *selected = true;
+ if(received != nil)
+ *received = false;
+ runtime_unlock(c);
+}
+
+// The compiler generates a call to __go_send_small to send a value 8
+// bytes or smaller.
+void
+__go_send_small(ChanType *t, Hchan* c, uint64 val)
+{
+ union
+ {
+ byte b[sizeof(uint64)];
+ uint64 v;
+ } u;
+ byte *p;
+
+ u.v = val;
+#ifndef WORDS_BIGENDIAN
+ p = u.b;
+#else
+ p = u.b + sizeof(uint64) - t->__element_type->__size;
+#endif
+ runtime_chansend(t, c, p, nil);
+}
+
+// The compiler generates a call to __go_send_big to send a value
+// larger than 8 bytes or smaller.
+void
+__go_send_big(ChanType *t, Hchan* c, byte* p)
+{
+ runtime_chansend(t, c, p, nil);
+}
+
+// The compiler generates a call to __go_receive_small to receive a
+// value 8 bytes or smaller.
+uint64
+__go_receive_small(ChanType *t, Hchan* c)
+{
+ union {
+ byte b[sizeof(uint64)];
+ uint64 v;
+ } u;
+ byte *p;
+
+ u.v = 0;
+#ifndef WORDS_BIGENDIAN
+ p = u.b;
+#else
+ p = u.b + sizeof(uint64) - t->__element_type->__size;
+#endif
+ runtime_chanrecv(t, c, p, nil, nil);
+ return u.v;
+}
+
+// The compiler generates a call to __go_receive_big to receive a
+// value larger than 8 bytes.
+void
+__go_receive_big(ChanType *t, Hchan* c, byte* p)
+{
+ runtime_chanrecv(t, c, p, nil, nil);
+}
+
+_Bool runtime_chanrecv2(ChanType *t, Hchan* c, byte* p)
+ __asm__("runtime.chanrecv2");
+
+_Bool
+runtime_chanrecv2(ChanType *t, Hchan* c, byte* p)
+{
+ bool received;
+
+ runtime_chanrecv(t, c, p, nil, &received);
+ return received;
+}
+
+// func selectnbsend(c chan any, elem any) bool
+//
+// compiler implements
+//
+// select {
+// case c <- v:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if selectnbsend(c, v) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+_Bool
+runtime_selectnbsend(ChanType *t, Hchan *c, byte *p)
+{
+ bool res;
+
+ runtime_chansend(t, c, p, &res);
+ return res;
+}
+
+// func selectnbrecv(elem *any, c chan any) bool
+//
+// compiler implements
+//
+// select {
+// case v = <-c:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if selectnbrecv(&v, c) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+_Bool
+runtime_selectnbrecv(ChanType *t, byte *v, Hchan *c)
+{
+ bool selected;
+
+ runtime_chanrecv(t, c, v, &selected, nil);
+ return selected;
+}
+
+// func selectnbrecv2(elem *any, ok *bool, c chan any) bool
+//
+// compiler implements
+//
+// select {
+// case v, ok = <-c:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if c != nil && selectnbrecv2(&v, &ok, c) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+_Bool
+runtime_selectnbrecv2(ChanType *t, byte *v, _Bool *received, Hchan *c)
+{
+ bool selected;
+ bool r;
+
+ r = false;
+ runtime_chanrecv(t, c, v, &selected, received == nil ? nil : &r);
+ if(received != nil)
+ *received = r;
+ return selected;
+}
+
+// For reflect:
+// func chansend(c chan, val iword, nb bool) (selected bool)
+// where an iword is the same word an interface value would use:
+// the actual data if it fits, or else a pointer to the data.
+
+_Bool reflect_chansend(ChanType *, Hchan *, uintptr, _Bool)
+ __asm__("reflect.chansend");
+
+_Bool
+reflect_chansend(ChanType *t, Hchan *c, uintptr val, _Bool nb)
+{
+ bool selected;
+ bool *sp;
+ byte *vp;
+
+ if(nb) {
+ selected = false;
+ sp = (bool*)&selected;
+ } else {
+ selected = true;
+ sp = nil;
+ }
+ if(__go_is_pointer_type(t->__element_type))
+ vp = (byte*)&val;
+ else
+ vp = (byte*)val;
+ runtime_chansend(t, c, vp, sp);
+ return selected;
+}
+
+// For reflect:
+// func chanrecv(c chan, nb bool) (val iword, selected, received bool)
+// where an iword is the same word an interface value would use:
+// the actual data if it fits, or else a pointer to the data.
+
+struct chanrecv_ret
+{
+ uintptr val;
+ _Bool selected;
+ _Bool received;
+};
+
+struct chanrecv_ret reflect_chanrecv(ChanType *, Hchan *, _Bool)
+ __asm__("reflect.chanrecv");
+
+struct chanrecv_ret
+reflect_chanrecv(ChanType *t, Hchan *c, _Bool nb)
+{
+ struct chanrecv_ret ret;
+ byte *vp;
+ bool *sp;
+ bool selected;
+ bool received;
+
+ if(nb) {
+ selected = false;
+ sp = &selected;
+ } else {
+ ret.selected = true;
+ sp = nil;
+ }
+ received = false;
+ if(__go_is_pointer_type(t->__element_type)) {
+ vp = (byte*)&ret.val;
+ } else {
+ vp = runtime_mal(t->__element_type->__size);
+ ret.val = (uintptr)vp;
+ }
+ runtime_chanrecv(t, c, vp, sp, &received);
+ if(nb)
+ ret.selected = selected;
+ ret.received = received;
+ return ret;
+}
+
+static void newselect(int32, Select**);
+
+// newselect(size uint32) (sel *byte);
+
+void* runtime_newselect(int) __asm__("runtime.newselect");
+
+void*
+runtime_newselect(int size)
+{
+ Select *sel;
+
+ newselect(size, &sel);
+ return (void*)sel;
+}
+
+static void
+newselect(int32 size, Select **selp)
+{
+ 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);
+ *selp = sel;
+
+ if(debug)
+ runtime_printf("newselect s=%p size=%d\n", sel, size);
+}
+
+// cut in half to give stack a chance to split
+static void selectsend(Select *sel, Hchan *c, int index, void *elem);
+
+// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
+
+void runtime_selectsend(Select *, Hchan *, void *, int)
+ __asm__("runtime.selectsend");
+
+void
+runtime_selectsend(Select *sel, Hchan *c, void *elem, int index)
+{
+ // nil cases do not compete
+ if(c == nil)
+ return;
+
+ selectsend(sel, c, index, elem);
+}
+
+static void
+selectsend(Select *sel, Hchan *c, int index, void *elem)
+{
+ 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->index = index;
+ cas->chan = c;
+ cas->kind = CaseSend;
+ cas->sg.elem = elem;
+
+ if(debug)
+ runtime_printf("selectsend s=%p index=%d chan=%p\n",
+ sel, cas->index, cas->chan);
+}
+
+// cut in half to give stack a chance to split
+static void selectrecv(Select *sel, Hchan *c, int index, void *elem, bool*);
+
+// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
+
+void runtime_selectrecv(Select *, Hchan *, void *, int)
+ __asm__("runtime.selectrecv");
+
+void
+runtime_selectrecv(Select *sel, Hchan *c, void *elem, int index)
+{
+ // nil cases do not compete
+ if(c == nil)
+ return;
+
+ selectrecv(sel, c, index, elem, nil);
+}
+
+// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
+
+void runtime_selectrecv2(Select *, Hchan *, void *, bool *, int)
+ __asm__("runtime.selectrecv2");
+
+void
+runtime_selectrecv2(Select *sel, Hchan *c, void *elem, bool *received, int index)
+{
+ // nil cases do not compete
+ if(c == nil)
+ return;
+
+ selectrecv(sel, c, index, elem, received);
+}
+
+static void
+selectrecv(Select *sel, Hchan *c, int index, void *elem, bool *received)
+{
+ 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->index = index;
+ cas->chan = c;
+
+ cas->kind = CaseRecv;
+ cas->sg.elem = elem;
+ cas->receivedp = received;
+
+ if(debug)
+ runtime_printf("selectrecv s=%p index=%d chan=%p\n",
+ sel, cas->index, cas->chan);
+}
+
+// cut in half to give stack a chance to split
+static void selectdefault(Select*, int);
+
+// selectdefault(sel *byte) (selected bool);
+
+void runtime_selectdefault(Select *, int) __asm__("runtime.selectdefault");
+
+void
+runtime_selectdefault(Select *sel, int index)
+{
+ selectdefault(sel, index);
+}
+
+static void
+selectdefault(Select *sel, int index)
+{
+ 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->index = index;
+ cas->chan = nil;
+
+ cas->kind = CaseDefault;
+
+ if(debug)
+ runtime_printf("selectdefault s=%p index=%d\n",
+ sel, cas->index);
+}
+
+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)
+{
+ uint32 i;
+ Hchan *c, *c0;
+
+ c = nil;
+ for(i=sel->ncase; i-->0;) {
+ c0 = sel->lockorder[i];
+ if(c0 && c0 != c) {
+ c = c0;
+ runtime_unlock(c);
+ }
+ }
+}
+
+void
+runtime_block(void)
+{
+ G *g;
+
+ g = runtime_g();
+ g->status = Gwaiting; // forever
+ g->waitreason = "select (no cases)";
+ runtime_gosched();
+}
+
+static int selectgo(Select**);
+
+// selectgo(sel *byte);
+
+int runtime_selectgo(Select *) __asm__("runtime.selectgo");
+
+int
+runtime_selectgo(Select *sel)
+{
+ return selectgo(&sel);
+}
+
+static int
+selectgo(Select **selp)
+{
+ Select *sel;
+ uint32 o, i, j;
+ Scase *cas, *dfl;
+ Hchan *c;
+ SudoG *sg;
+ G *gp;
+ int index;
+ G *g;
+
+ sel = *selp;
+ if(runtime_gcwaiting)
+ runtime_gosched();
+
+ if(debug)
+ runtime_printf("select: sel=%p\n", sel);
+
+ g = runtime_g();
+
+ // 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.
+ for(i=0; i<sel->ncase; i++) {
+ c = sel->scase[i].chan;
+ for(j=i; j>0 && sel->lockorder[j-1] >= c; j--)
+ sel->lockorder[j] = sel->lockorder[j-1];
+ sel->lockorder[j] = c;
+ }
+ 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(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
+ 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->selgen = g->selgen;
+
+ switch(cas->kind) {
+ case CaseRecv:
+ enqueue(&c->recvq, sg);
+ break;
+
+ case CaseSend:
+ enqueue(&c->sendq, sg);
+ break;
+ }
+ }
+
+ g->param = nil;
+ g->status = Gwaiting;
+ g->waitreason = "select";
+ selunlock(sel);
+ runtime_gosched();
+
+ 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: shouldnt 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;
+ }
+
+ selunlock(sel);
+ goto retc;
+
+asyncrecv:
+ // can receive from buffer
+ if(cas->receivedp != nil)
+ *cas->receivedp = true;
+ if(cas->sg.elem != nil)
+ runtime_memmove(cas->sg.elem, chanbuf(c, c->recvx), c->elemsize);
+ runtime_memclr(chanbuf(c, c->recvx), c->elemsize);
+ if(++c->recvx == c->dataqsiz)
+ c->recvx = 0;
+ c->qcount--;
+ sg = dequeue(&c->sendq);
+ if(sg != nil) {
+ gp = sg->g;
+ selunlock(sel);
+ runtime_ready(gp);
+ } else {
+ selunlock(sel);
+ }
+ goto retc;
+
+asyncsend:
+ // can send to buffer
+ runtime_memmove(chanbuf(c, c->sendx), cas->sg.elem, c->elemsize);
+ if(++c->sendx == c->dataqsiz)
+ c->sendx = 0;
+ c->qcount++;
+ sg = dequeue(&c->recvq);
+ if(sg != nil) {
+ gp = sg->g;
+ selunlock(sel);
+ runtime_ready(gp);
+ } else {
+ selunlock(sel);
+ }
+ goto retc;
+
+syncrecv:
+ // can receive from sleeping sender (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)
+ runtime_memmove(cas->sg.elem, sg->elem, c->elemsize);
+ gp = sg->g;
+ gp->param = sg;
+ 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)
+ runtime_memclr(cas->sg.elem, c->elemsize);
+ goto retc;
+
+syncsend:
+ // can send to sleeping receiver (sg)
+ selunlock(sel);
+ if(debug)
+ runtime_printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
+ if(sg->elem != nil)
+ runtime_memmove(sg->elem, cas->sg.elem, c->elemsize);
+ gp = sg->g;
+ gp->param = sg;
+ runtime_ready(gp);
+
+retc:
+ // return index corresponding to chosen case
+ index = cas->index;
+ runtime_free(sel);
+ return index;
+
+sclose:
+ // send on closed channel
+ selunlock(sel);
+ runtime_panicstring("send on closed channel");
+ return 0; // not reached
+}
+
+// closechan(sel *byte);
+void
+runtime_closechan(Hchan *c)
+{
+ SudoG *sg;
+ G* gp;
+
+ if(c == nil)
+ runtime_panicstring("close of nil channel");
+
+ if(runtime_gcwaiting)
+ runtime_gosched();
+
+ runtime_lock(c);
+ if(c->closed) {
+ runtime_unlock(c);
+ runtime_panicstring("close of closed channel");
+ }
+
+ c->closed = true;
+
+ // release all readers
+ for(;;) {
+ sg = dequeue(&c->recvq);
+ if(sg == nil)
+ break;
+ gp = sg->g;
+ gp->param = nil;
+ runtime_ready(gp);
+ }
+
+ // release all writers
+ for(;;) {
+ sg = dequeue(&c->sendq);
+ if(sg == nil)
+ break;
+ gp = sg->g;
+ gp->param = nil;
+ runtime_ready(gp);
+ }
+
+ runtime_unlock(c);
+}
+
+void
+__go_builtin_close(Hchan *c)
+{
+ runtime_closechan(c);
+}
+
+// For reflect
+// func chanclose(c chan)
+
+void reflect_chanclose(uintptr) __asm__("reflect.chanclose");
+
+void
+reflect_chanclose(uintptr c)
+{
+ runtime_closechan((Hchan*)c);
+}
+
+// For reflect
+// func chanlen(c chan) (len int32)
+
+int32 reflect_chanlen(uintptr) __asm__("reflect.chanlen");
+
+int32
+reflect_chanlen(uintptr ca)
+{
+ Hchan *c;
+ int32 len;
+
+ c = (Hchan*)ca;
+ if(c == nil)
+ len = 0;
+ else
+ len = c->qcount;
+ return len;
+}
+
+int
+__go_chan_len(Hchan *c)
+{
+ return reflect_chanlen((uintptr)c);
+}
+
+// For reflect
+// func chancap(c chan) (cap int32)
+
+int32 reflect_chancap(uintptr) __asm__("reflect.chancap");
+
+int32
+reflect_chancap(uintptr ca)
+{
+ Hchan *c;
+ int32 cap;
+
+ c = (Hchan*)ca;
+ if(c == nil)
+ cap = 0;
+ else
+ cap = c->dataqsiz;
+ return cap;
+}
+
+int
+__go_chan_cap(Hchan *c)
+{
+ return reflect_chancap((uintptr)c);
+}
+
+static SudoG*
+dequeue(WaitQ *q)
+{
+ SudoG *sgp;
+
+loop:
+ sgp = q->first;
+ if(sgp == nil)
+ return nil;
+ q->first = sgp->link;
+
+ // if sgp is stale, ignore it
+ if(sgp->selgen != NOSELGEN &&
+ (sgp->selgen != sgp->g->selgen ||
+ !runtime_cas(&sgp->g->selgen, sgp->selgen, sgp->selgen + 2))) {
+ //prints("INVALID PSEUDOG POINTER\n");
+ goto loop;
+ }
+
+ return sgp;
+}
+
+static void
+dequeueg(WaitQ *q)
+{
+ SudoG **l, *sgp, *prevsgp;
+ G *g;
+
+ g = runtime_g();
+ 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;
+}