diff options
Diffstat (limited to 'libgo/go/runtime/select.go')
-rw-r--r-- | libgo/go/runtime/select.go | 651 |
1 files changed, 0 insertions, 651 deletions
diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go deleted file mode 100644 index f735a71..0000000 --- a/libgo/go/runtime/select.go +++ /dev/null @@ -1,651 +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 - -// This file contains the implementation of Go select statements. - -import "unsafe" - -const ( - debugSelect = false -) - -var ( - chansendpc = funcPC(chansend) - chanrecvpc = funcPC(chanrecv) -) - -func selectsize(size uintptr) uintptr { - selsize := unsafe.Sizeof(_select{}) + - (size-1)*unsafe.Sizeof(_select{}.scase[0]) + - size*unsafe.Sizeof(*_select{}.lockorder) + - size*unsafe.Sizeof(*_select{}.pollorder) - return round(selsize, _Int64Align) -} - -func newselect(sel *_select, selsize int64, size int32) { - if selsize != int64(selectsize(uintptr(size))) { - print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n") - gothrow("bad select size") - } - sel.tcase = uint16(size) - sel.ncase = 0 - sel.lockorder = (**hchan)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(_select{}.scase[0]))) - sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*_select{}.lockorder))) - - if debugSelect { - print("newselect s=", sel, " size=", size, "\n") - } -} - -//go:nosplit -func selectsend(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) { - // nil cases do not compete - if c != nil { - selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel))) - } - return -} - -// cut in half to give stack a chance to split -func selectsendImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) { - i := sel.ncase - if i >= sel.tcase { - gothrow("selectsend: too many cases") - } - sel.ncase = i + 1 - cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0]))) - - cas.pc = pc - cas._chan = c - cas.so = uint16(so) - cas.kind = _CaseSend - cas.elem = elem - - if debugSelect { - print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n") - } -} - -//go:nosplit -func selectrecv(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) { - // nil cases do not compete - if c != nil { - selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel))) - } - return -} - -//go:nosplit -func selectrecv2(sel *_select, c *hchan, elem unsafe.Pointer, received *bool) (selected bool) { - // nil cases do not compete - if c != nil { - selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel))) - } - return -} - -func selectrecvImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, so uintptr) { - i := sel.ncase - if i >= sel.tcase { - gothrow("selectrecv: too many cases") - } - sel.ncase = i + 1 - cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0]))) - cas.pc = pc - cas._chan = c - cas.so = uint16(so) - cas.kind = _CaseRecv - cas.elem = elem - cas.receivedp = received - - if debugSelect { - print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n") - } -} - -//go:nosplit -func selectdefault(sel *_select) (selected bool) { - selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel))) - return -} - -func selectdefaultImpl(sel *_select, callerpc uintptr, so uintptr) { - i := sel.ncase - if i >= sel.tcase { - gothrow("selectdefault: too many cases") - } - sel.ncase = i + 1 - cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0]))) - cas.pc = callerpc - cas._chan = nil - cas.so = uint16(so) - cas.kind = _CaseDefault - - if debugSelect { - print("selectdefault s=", sel, " pc=", hex(cas.pc), " so=", cas.so, "\n") - } -} - -func sellock(sel *_select) { - lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)} - lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice)) - var c *hchan - for _, c0 := range lockorder { - if c0 != nil && c0 != c { - c = c0 - lock(&c.lock) - } - } -} - -func selunlock(sel *_select) { - // 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 := int(sel.ncase) - r := 0 - lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), n, n} - lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice)) - // skip the default case - if n > 0 && lockorder[0] == nil { - r = 1 - } - for i := n - 1; i >= r; i-- { - c := lockorder[i] - if i > 0 && c == lockorder[i-1] { - continue // will unlock it on the next iteration - } - unlock(&c.lock) - } -} - -func selparkcommit(gp *g, sel *_select) bool { - selunlock(sel) - return true -} - -func block() { - gopark(nil, nil, "select (no cases)") // forever -} - -// overwrites return pc on stack to signal which case of the select -// to run, so cannot appear at the top of a split stack. -//go:nosplit -func selectgo(sel *_select) { - pc, offset := selectgoImpl(sel) - *(*bool)(add(unsafe.Pointer(&sel), uintptr(offset))) = true - setcallerpc(unsafe.Pointer(&sel), pc) -} - -// selectgoImpl returns scase.pc and scase.so for the select -// case which fired. -func selectgoImpl(sel *_select) (uintptr, uint16) { - if debugSelect { - print("select: sel=", sel, "\n") - } - - scaseslice := sliceStruct{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)} - scases := *(*[]scase)(unsafe.Pointer(&scaseslice)) - - var t0 int64 - if blockprofilerate > 0 { - t0 = cputicks() - for i := 0; i < int(sel.ncase); i++ { - scases[i].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 - pollslice := sliceStruct{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)} - pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice)) - for i := 0; i < int(sel.ncase); i++ { - pollorder[i] = uint16(i) - } - for i := 1; i < int(sel.ncase); i++ { - o := pollorder[i] - j := int(fastrand1()) % (i + 1) - pollorder[i] = pollorder[j] - 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. - lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)} - lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice)) - for i := 0; i < int(sel.ncase); i++ { - j := i - c := scases[j]._chan - for j > 0 && lockorder[(j-1)/2].sortkey() < c.sortkey() { - k := (j - 1) / 2 - lockorder[j] = lockorder[k] - j = k - } - lockorder[j] = c - } - for i := int(sel.ncase) - 1; i >= 0; i-- { - c := lockorder[i] - lockorder[i] = lockorder[0] - j := 0 - for { - k := j*2 + 1 - if k >= i { - break - } - if k+1 < i && lockorder[k].sortkey() < lockorder[k+1].sortkey() { - k++ - } - if c.sortkey() < lockorder[k].sortkey() { - lockorder[j] = lockorder[k] - j = k - continue - } - break - } - lockorder[j] = c - } - /* - for i := 0; i+1 < int(sel.ncase); i++ { - if lockorder[i].sortkey() > lockorder[i+1].sortkey() { - print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n") - gothrow("select: broken sort") - } - } - */ - - // lock all the channels involved in the select - sellock(sel) - - var ( - gp *g - done uint32 - sg *sudog - c *hchan - k *scase - sglist *sudog - sgnext *sudog - ) - -loop: - // pass 1 - look for something already waiting - var dfl *scase - var cas *scase - for i := 0; i < int(sel.ncase); i++ { - cas = &scases[pollorder[i]] - c = cas._chan - - switch cas.kind { - case _CaseRecv: - if c.dataqsiz > 0 { - if c.qcount > 0 { - goto asyncrecv - } - } else { - sg = c.sendq.dequeue() - if sg != nil { - goto syncrecv - } - } - if c.closed != 0 { - goto rclose - } - - case _CaseSend: - if raceenabled { - racereadpc(unsafe.Pointer(c), cas.pc, chansendpc) - } - if c.closed != 0 { - goto sclose - } - if c.dataqsiz > 0 { - if c.qcount < c.dataqsiz { - goto asyncsend - } - } else { - sg = c.recvq.dequeue() - if sg != nil { - goto syncsend - } - } - - case _CaseDefault: - dfl = cas - } - } - - if dfl != nil { - selunlock(sel) - cas = dfl - goto retc - } - - // pass 2 - enqueue on all chans - gp = getg() - done = 0 - for i := 0; i < int(sel.ncase); i++ { - cas = &scases[pollorder[i]] - c = cas._chan - sg := acquireSudog() - sg.g = gp - // Note: selectdone is adjusted for stack copies in stack.c:adjustsudogs - sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done))) - sg.elem = cas.elem - sg.releasetime = 0 - if t0 != 0 { - sg.releasetime = -1 - } - sg.waitlink = gp.waiting - gp.waiting = sg - - switch cas.kind { - case _CaseRecv: - c.recvq.enqueue(sg) - - case _CaseSend: - c.sendq.enqueue(sg) - } - } - - // wait for someone to wake us up - gp.param = nil - gopark(unsafe.Pointer(funcPC(selparkcommit)), unsafe.Pointer(sel), "select") - - // someone woke us up - sellock(sel) - sg = (*sudog)(gp.param) - gp.param = nil - - // pass 3 - dequeue from unsuccessful chans - // otherwise they stack up on quiet channels - // record the successful case, if any. - // We singly-linked up the SudoGs in case order, so when - // iterating through the linked list they are in reverse order. - cas = nil - sglist = gp.waiting - // Clear all selectdone and elem before unlinking from gp.waiting. - // They must be cleared before being put back into the sudog cache. - // Clear before unlinking, because if a stack copy happens after the unlink, - // they will not be updated, they will be left pointing to the old stack, - // which creates dangling pointers, which may be detected by the - // garbage collector. - for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink { - sg1.selectdone = nil - sg1.elem = nil - } - gp.waiting = nil - for i := int(sel.ncase) - 1; i >= 0; i-- { - k = &scases[pollorder[i]] - if sglist.releasetime > 0 { - k.releasetime = sglist.releasetime - } - if sg == sglist { - cas = k - } else { - c = k._chan - if k.kind == _CaseSend { - c.sendq.dequeueSudoG(sglist) - } else { - c.recvq.dequeueSudoG(sglist) - } - } - sgnext = sglist.waitlink - sglist.waitlink = nil - releaseSudog(sglist) - sglist = sgnext - } - - if cas == nil { - goto loop - } - - c = cas._chan - - if c.dataqsiz > 0 { - gothrow("selectgo: shouldn't happen") - } - - if debugSelect { - print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n") - } - - if cas.kind == _CaseRecv { - if cas.receivedp != nil { - *cas.receivedp = true - } - } - - if raceenabled { - if cas.kind == _CaseRecv && cas.elem != nil { - raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc) - } else if cas.kind == _CaseSend { - raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) - } - } - - selunlock(sel) - goto retc - -asyncrecv: - // can receive from buffer - if raceenabled { - if cas.elem != nil { - raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc) - } - raceacquire(chanbuf(c, c.recvx)) - racerelease(chanbuf(c, c.recvx)) - } - if cas.receivedp != nil { - *cas.receivedp = true - } - if cas.elem != nil { - memmove(cas.elem, chanbuf(c, c.recvx), uintptr(c.elemsize)) - } - memclr(chanbuf(c, c.recvx), uintptr(c.elemsize)) - c.recvx++ - if c.recvx == c.dataqsiz { - c.recvx = 0 - } - c.qcount-- - sg = c.sendq.dequeue() - if sg != nil { - gp = sg.g - selunlock(sel) - if sg.releasetime != 0 { - sg.releasetime = cputicks() - } - goready(gp) - } else { - selunlock(sel) - } - goto retc - -asyncsend: - // can send to buffer - if raceenabled { - raceacquire(chanbuf(c, c.sendx)) - racerelease(chanbuf(c, c.sendx)) - raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) - } - memmove(chanbuf(c, c.sendx), cas.elem, uintptr(c.elemsize)) - c.sendx++ - if c.sendx == c.dataqsiz { - c.sendx = 0 - } - c.qcount++ - sg = c.recvq.dequeue() - if sg != nil { - gp = sg.g - selunlock(sel) - if sg.releasetime != 0 { - sg.releasetime = cputicks() - } - goready(gp) - } else { - selunlock(sel) - } - goto retc - -syncrecv: - // can receive from sleeping sender (sg) - if raceenabled { - if cas.elem != nil { - raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc) - } - racesync(c, sg) - } - selunlock(sel) - if debugSelect { - print("syncrecv: sel=", sel, " c=", c, "\n") - } - if cas.receivedp != nil { - *cas.receivedp = true - } - if cas.elem != nil { - memmove(cas.elem, sg.elem, uintptr(c.elemsize)) - } - sg.elem = nil - gp = sg.g - gp.param = unsafe.Pointer(sg) - if sg.releasetime != 0 { - sg.releasetime = cputicks() - } - goready(gp) - goto retc - -rclose: - // read at end of closed channel - selunlock(sel) - if cas.receivedp != nil { - *cas.receivedp = false - } - if cas.elem != nil { - memclr(cas.elem, uintptr(c.elemsize)) - } - if raceenabled { - raceacquire(unsafe.Pointer(c)) - } - goto retc - -syncsend: - // can send to sleeping receiver (sg) - if raceenabled { - raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) - racesync(c, sg) - } - selunlock(sel) - if debugSelect { - print("syncsend: sel=", sel, " c=", c, "\n") - } - if sg.elem != nil { - memmove(sg.elem, cas.elem, uintptr(c.elemsize)) - } - sg.elem = nil - gp = sg.g - gp.param = unsafe.Pointer(sg) - if sg.releasetime != 0 { - sg.releasetime = cputicks() - } - goready(gp) - -retc: - if cas.releasetime > 0 { - blockevent(cas.releasetime-t0, 2) - } - return cas.pc, cas.so - -sclose: - // send on closed channel - selunlock(sel) - panic("send on closed channel") -} - -func (c *hchan) sortkey() uintptr { - // TODO(khr): if we have a moving garbage collector, we'll need to - // change this function. - return uintptr(unsafe.Pointer(c)) -} - -// A runtimeSelect is a single case passed to rselect. -// This must match ../reflect/value.go:/runtimeSelect -type runtimeSelect struct { - dir selectDir - typ unsafe.Pointer // channel type (not used here) - ch *hchan // channel - val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir) -} - -// These values must match ../reflect/value.go:/SelectDir. -type selectDir int - -const ( - _ selectDir = iota - selectSend // case Chan <- Send - selectRecv // case <-Chan: - selectDefault // default -) - -func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) { - // flagNoScan is safe here, because all objects are also referenced from cases. - size := selectsize(uintptr(len(cases))) - sel := (*_select)(mallocgc(size, nil, flagNoScan)) - newselect(sel, int64(size), int32(len(cases))) - r := new(bool) - for i := range cases { - rc := &cases[i] - switch rc.dir { - case selectDefault: - selectdefaultImpl(sel, uintptr(i), 0) - case selectSend: - if rc.ch == nil { - break - } - selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0) - case selectRecv: - if rc.ch == nil { - break - } - selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0) - } - } - - pc, _ := selectgoImpl(sel) - chosen = int(pc) - recvOK = *r - return -} - -func (q *waitq) dequeueSudoG(s *sudog) { - var prevsgp *sudog - l := &q.first - for { - sgp := *l - if sgp == nil { - return - } - if sgp == s { - *l = sgp.next - if q.last == sgp { - q.last = prevsgp - } - s.next = nil - return - } - l = &sgp.next - prevsgp = sgp - } -} |