diff options
Diffstat (limited to 'gcc-4.8.1/libgo/runtime/chan.c')
-rw-r--r-- | gcc-4.8.1/libgo/runtime/chan.c | 1430 |
1 files changed, 0 insertions, 1430 deletions
diff --git a/gcc-4.8.1/libgo/runtime/chan.c b/gcc-4.8.1/libgo/runtime/chan.c deleted file mode 100644 index a79ee9e18..000000000 --- a/gcc-4.8.1/libgo/runtime/chan.c +++ /dev/null @@ -1,1430 +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. - -#include "runtime.h" -#include "arch.h" -#include "go-type.h" -#include "race.h" -#include "malloc.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; - int64 releasetime; - byte* elem; // data element -}; - -struct WaitQ -{ - SudoG* first; - SudoG* last; -}; - -struct Hchan -{ - uintgo qcount; // total data in the q - uintgo dataqsiz; // size of the circular q - uint16 elemsize; - bool closed; - uint8 elemalign; - uintgo sendx; // send index - uintgo 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*); -static void racesync(Hchan*, SudoG*); - -Hchan* -runtime_makechan_c(ChanType *t, int64 hint) -{ - Hchan *c; - uintptr n; - const Type *elem; - - elem = t->__element_type; - - // compiler checks this but be safe. - if(elem->__size >= (1<<16)) - runtime_throw("makechan: invalid channel element type"); - - if(hint < 0 || (intgo)hint != hint || (elem->__size > 0 && (uintptr)hint > MaxMem / 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, (int64)c->dataqsiz); - - return c; -} - -// For reflect -// func makechan(typ *ChanType, size uint64) (chan) -uintptr reflect_makechan(ChanType *, uint64) - __asm__ (GOSYM_PREFIX "reflect.makechan"); - -uintptr -reflect_makechan(ChanType *t, uint64 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, void *pc) -{ - SudoG *sg; - SudoG mysg; - G* gp; - int64 t0; - G* g; - - g = runtime_g(); - - if(c == nil) { - USED(t); - if(pres != nil) { - *pres = false; - return; - } - runtime_park(nil, nil, "chan send (nil chan)"); - return; // not reached - } - - if(runtime_gcwaiting) - runtime_gosched(); - - if(debug) { - runtime_printf("chansend: chan=%p\n", c); - } - - t0 = 0; - mysg.releasetime = 0; - if(runtime_blockprofilerate > 0) { - t0 = runtime_cputicks(); - mysg.releasetime = -1; - } - - runtime_lock(c); - // TODO(dvyukov): add similar instrumentation to select. - if(raceenabled) - runtime_racereadpc(c, pc, runtime_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) - runtime_memmove(sg->elem, ep, c->elemsize); - if(sg->releasetime) - sg->releasetime = runtime_cputicks(); - 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; - enqueue(&c->sendq, &mysg); - runtime_park(runtime_unlock, 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; - -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; - enqueue(&c->sendq, &mysg); - runtime_park(runtime_unlock, c, "chan send"); - - runtime_lock(c); - goto asynch; - } - - if(raceenabled) - runtime_racerelease(chanbuf(c, c->sendx)); - - 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); - if(sg->releasetime) - sg->releasetime = runtime_cputicks(); - runtime_ready(gp); - } else - runtime_unlock(c); - if(pres != nil) - *pres = true; - if(mysg.releasetime > 0) - runtime_blockevent(mysg.releasetime - t0, 2); - 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; - int64 t0; - 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; - } - runtime_park(nil, nil, "chan receive (nil chan)"); - return; // 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) - runtime_memmove(ep, sg->elem, c->elemsize); - gp = sg->g; - gp->param = sg; - if(sg->releasetime) - sg->releasetime = runtime_cputicks(); - 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; - enqueue(&c->recvq, &mysg); - runtime_park(runtime_unlock, 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; - -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; - enqueue(&c->recvq, &mysg); - runtime_park(runtime_unlock, c, "chan receive"); - - runtime_lock(c); - goto asynch; - } - - if(raceenabled) - runtime_raceacquire(chanbuf(c, c->recvx)); - - 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); - if(sg->releasetime) - sg->releasetime = runtime_cputicks(); - runtime_ready(gp); - } else - runtime_unlock(c); - - if(selected != nil) - *selected = true; - if(received != nil) - *received = true; - if(mysg.releasetime > 0) - runtime_blockevent(mysg.releasetime - t0, 2); - return; - -closed: - if(ep != nil) - runtime_memclr(ep, c->elemsize); - if(selected != nil) - *selected = true; - if(received != nil) - *received = false; - if(raceenabled) - runtime_raceacquire(c); - runtime_unlock(c); - if(mysg.releasetime > 0) - runtime_blockevent(mysg.releasetime - t0, 2); -} - -// 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, runtime_getcallerpc(&t)); -} - -// 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, runtime_getcallerpc(&t)); -} - -// 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__ (GOSYM_PREFIX "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, runtime_getcallerpc(&t)); - 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__ (GOSYM_PREFIX "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, runtime_getcallerpc(&t)); - 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__ (GOSYM_PREFIX "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(int32) __asm__ (GOSYM_PREFIX "runtime.newselect"); - -void* -runtime_newselect(int32 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 *, int32) - __asm__ (GOSYM_PREFIX "runtime.selectsend"); - -void -runtime_selectsend(Select *sel, Hchan *c, void *elem, int32 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 *, int32) - __asm__ (GOSYM_PREFIX "runtime.selectrecv"); - -void -runtime_selectrecv(Select *sel, Hchan *c, void *elem, int32 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 *, int32) - __asm__ (GOSYM_PREFIX "runtime.selectrecv2"); - -void -runtime_selectrecv2(Select *sel, Hchan *c, void *elem, bool *received, int32 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 *, int32) __asm__ (GOSYM_PREFIX "runtime.selectdefault"); - -void -runtime_selectdefault(Select *sel, int32 index) -{ - selectdefault(sel, index); -} - -static void -selectdefault(Select *sel, int32 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) -{ - runtime_park(nil, nil, "select (no cases)"); // forever -} - -static int selectgo(Select**); - -// selectgo(sel *byte); - -int runtime_selectgo(Select *) __asm__ (GOSYM_PREFIX "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; - runtime_park((void(*)(Lock*))selunlock, (Lock*)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: 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(raceenabled) - runtime_raceacquire(chanbuf(c, c->recvx)); - 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 - if(raceenabled) - runtime_racerelease(chanbuf(c, c->sendx)); - 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) - if(raceenabled) - 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) - 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); - if(raceenabled) - runtime_raceacquire(c); - goto retc; - -syncsend: - // can send to sleeping receiver (sg) - if(raceenabled) - racesync(c, 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 -} - -// This struct must match ../reflect/value.go:/runtimeSelect. -typedef struct runtimeSelect runtimeSelect; -struct runtimeSelect -{ - uintptr dir; - ChanType *typ; - Hchan *ch; - uintptr val; -}; - -// This enum must match ../reflect/value.go:/SelectDir. -enum SelectDir { - SelectSend = 1, - SelectRecv, - SelectDefault, -}; - -struct rselect_ret { - intgo chosen; - uintptr word; - bool recvOK; -}; - -// func rselect(cases []runtimeSelect) (chosen int, word uintptr, recvOK bool) - -struct rselect_ret reflect_rselect(Slice) - __asm__ (GOSYM_PREFIX "reflect.rselect"); - -struct rselect_ret -reflect_rselect(Slice cases) -{ - struct rselect_ret ret; - int32 i; - Select *sel; - runtimeSelect* rcase, *rc; - void *elem; - void *recvptr; - uintptr maxsize; - bool onlyptr; - - ret.chosen = -1; - ret.word = 0; - ret.recvOK = false; - - maxsize = 0; - onlyptr = true; - rcase = (runtimeSelect*)cases.__values; - for(i=0; i<cases.__count; i++) { - rc = &rcase[i]; - if(rc->dir == SelectRecv && rc->ch != nil) { - if(maxsize < rc->typ->__element_type->__size) - maxsize = rc->typ->__element_type->__size; - if(!__go_is_pointer_type(rc->typ->__element_type)) - onlyptr = false; - } - } - - recvptr = nil; - if(!onlyptr) - recvptr = runtime_mal(maxsize); - - newselect(cases.__count, &sel); - for(i=0; i<cases.__count; i++) { - rc = &rcase[i]; - switch(rc->dir) { - case SelectDefault: - selectdefault(sel, i); - break; - case SelectSend: - if(rc->ch == nil) - break; - if(!__go_is_pointer_type(rc->typ->__element_type)) - elem = (void*)rc->val; - else - elem = (void*)&rc->val; - selectsend(sel, rc->ch, i, elem); - break; - case SelectRecv: - if(rc->ch == nil) - break; - if(!__go_is_pointer_type(rc->typ->__element_type)) - elem = recvptr; - else - elem = &ret.word; - selectrecv(sel, rc->ch, i, elem, &ret.recvOK); - break; - } - } - - ret.chosen = (intgo)(uintptr)selectgo(&sel); - if(rcase[ret.chosen].dir == SelectRecv && !__go_is_pointer_type(rcase[ret.chosen].typ->__element_type)) - ret.word = (uintptr)recvptr; - - return ret; -} - -// 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"); - } - - if(raceenabled) { - runtime_racewritepc(c, runtime_getcallerpc(&c), 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; - 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__ (GOSYM_PREFIX "reflect.chanclose"); - -void -reflect_chanclose(uintptr c) -{ - runtime_closechan((Hchan*)c); -} - -// For reflect -// func chanlen(c chan) (len int) - -intgo reflect_chanlen(uintptr) __asm__ (GOSYM_PREFIX "reflect.chanlen"); - -intgo -reflect_chanlen(uintptr ca) -{ - Hchan *c; - intgo len; - - c = (Hchan*)ca; - if(c == nil) - len = 0; - else - len = c->qcount; - return len; -} - -intgo -__go_chan_len(Hchan *c) -{ - return reflect_chanlen((uintptr)c); -} - -// For reflect -// func chancap(c chan) (cap intgo) - -intgo reflect_chancap(uintptr) __asm__ (GOSYM_PREFIX "reflect.chancap"); - -intgo -reflect_chancap(uintptr ca) -{ - Hchan *c; - intgo cap; - - c = (Hchan*)ca; - if(c == nil) - cap = 0; - else - cap = c->dataqsiz; - return cap; -} - -intgo -__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; -} - -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)); -} |