1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
7 // This file contains the implementation of Go select statements.
16 chansendpc = funcPC(chansend)
17 chanrecvpc = funcPC(chanrecv)
20 func selectsize(size uintptr) uintptr {
21 selsize := unsafe.Sizeof(_select{}) +
22 (size-1)*unsafe.Sizeof(_select{}.scase[0]) +
23 size*unsafe.Sizeof(*_select{}.lockorder) +
24 size*unsafe.Sizeof(*_select{}.pollorder)
25 return round(selsize, _Int64Align)
28 func newselect(sel *_select, selsize int64, size int32) {
29 if selsize != int64(selectsize(uintptr(size))) {
30 print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n")
31 gothrow("bad select size")
33 sel.tcase = uint16(size)
35 sel.lockorder = (**hchan)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(_select{}.scase[0])))
36 sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*_select{}.lockorder)))
39 print("newselect s=", sel, " size=", size, "\n")
44 func selectsend(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) {
45 // nil cases do not compete
47 selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
52 // cut in half to give stack a chance to split
53 func selectsendImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) {
56 gothrow("selectsend: too many cases")
59 cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
68 print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n")
73 func selectrecv(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) {
74 // nil cases do not compete
76 selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
82 func selectrecv2(sel *_select, c *hchan, elem unsafe.Pointer, received *bool) (selected bool) {
83 // nil cases do not compete
85 selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
90 func selectrecvImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, so uintptr) {
93 gothrow("selectrecv: too many cases")
96 cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
102 cas.receivedp = received
105 print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n")
110 func selectdefault(sel *_select) (selected bool) {
111 selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
115 func selectdefaultImpl(sel *_select, callerpc uintptr, so uintptr) {
118 gothrow("selectdefault: too many cases")
121 cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
125 cas.kind = _CaseDefault
128 print("selectdefault s=", sel, " pc=", hex(cas.pc), " so=", cas.so, "\n")
132 func sellock(sel *_select) {
133 lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
134 lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
136 for _, c0 := range lockorder {
137 if c0 != nil && c0 != c {
144 func selunlock(sel *_select) {
145 // We must be very careful here to not touch sel after we have unlocked
146 // the last lock, because sel can be freed right after the last unlock.
147 // Consider the following situation.
148 // First M calls runtime·park() in runtime·selectgo() passing the sel.
149 // Once runtime·park() has unlocked the last lock, another M makes
150 // the G that calls select runnable again and schedules it for execution.
151 // When the G runs on another M, it locks all the locks and frees sel.
152 // Now if the first M touches sel, it will access freed memory.
155 lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), n, n}
156 lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
157 // skip the default case
158 if n > 0 && lockorder[0] == nil {
161 for i := n - 1; i >= r; i-- {
163 if i > 0 && c == lockorder[i-1] {
164 continue // will unlock it on the next iteration
170 func selparkcommit(gp *g, sel unsafe.Pointer) bool {
171 selunlock((*_select)(sel))
176 gopark(nil, nil, "select (no cases)") // forever
179 // overwrites return pc on stack to signal which case of the select
180 // to run, so cannot appear at the top of a split stack.
182 func selectgo(sel *_select) {
183 pc, offset := selectgoImpl(sel)
184 *(*bool)(add(unsafe.Pointer(&sel), uintptr(offset))) = true
185 setcallerpc(unsafe.Pointer(&sel), pc)
188 // selectgoImpl returns scase.pc and scase.so for the select
190 func selectgoImpl(sel *_select) (uintptr, uint16) {
192 print("select: sel=", sel, "\n")
195 scaseslice := sliceStruct{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
196 scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
199 if blockprofilerate > 0 {
201 for i := 0; i < int(sel.ncase); i++ {
202 scases[i].releasetime = -1
206 // The compiler rewrites selects that statically have
207 // only 0 or 1 cases plus default into simpler constructs.
208 // The only way we can end up with such small sel.ncase
209 // values here is for a larger select in which most channels
210 // have been nilled out. The general code handles those
211 // cases correctly, and they are rare enough not to bother
212 // optimizing (and needing to test).
214 // generate permuted order
215 pollslice := sliceStruct{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)}
216 pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice))
217 for i := 0; i < int(sel.ncase); i++ {
218 pollorder[i] = uint16(i)
220 for i := 1; i < int(sel.ncase); i++ {
222 j := int(fastrand1()) % (i + 1)
223 pollorder[i] = pollorder[j]
227 // sort the cases by Hchan address to get the locking order.
228 // simple heap sort, to guarantee n log n time and constant stack footprint.
229 lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
230 lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
231 for i := 0; i < int(sel.ncase); i++ {
234 for j > 0 && lockorder[(j-1)/2].sortkey() < c.sortkey() {
236 lockorder[j] = lockorder[k]
241 for i := int(sel.ncase) - 1; i >= 0; i-- {
243 lockorder[i] = lockorder[0]
250 if k+1 < i && lockorder[k].sortkey() < lockorder[k+1].sortkey() {
253 if c.sortkey() < lockorder[k].sortkey() {
254 lockorder[j] = lockorder[k]
263 for i := 0; i+1 < int(sel.ncase); i++ {
264 if lockorder[i].sortkey() > lockorder[i+1].sortkey() {
265 print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n")
266 gothrow("select: broken sort")
271 // lock all the channels involved in the select
285 // pass 1 - look for something already waiting
288 for i := 0; i < int(sel.ncase); i++ {
289 cas = &scases[pollorder[i]]
299 sg = c.sendq.dequeue()
310 racereadpc(unsafe.Pointer(c), cas.pc, chansendpc)
316 if c.qcount < c.dataqsiz {
320 sg = c.recvq.dequeue()
337 // pass 2 - enqueue on all chans
340 for i := 0; i < int(sel.ncase); i++ {
341 cas = &scases[pollorder[i]]
345 // Note: selectdone is adjusted for stack copies in stack.c:adjustsudogs
346 sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done)))
352 sg.waitlink = gp.waiting
364 // wait for someone to wake us up
366 gopark(selparkcommit, unsafe.Pointer(sel), "select")
368 // someone woke us up
370 sg = (*sudog)(gp.param)
373 // pass 3 - dequeue from unsuccessful chans
374 // otherwise they stack up on quiet channels
375 // record the successful case, if any.
376 // We singly-linked up the SudoGs in case order, so when
377 // iterating through the linked list they are in reverse order.
380 // Clear all selectdone and elem before unlinking from gp.waiting.
381 // They must be cleared before being put back into the sudog cache.
382 // Clear before unlinking, because if a stack copy happens after the unlink,
383 // they will not be updated, they will be left pointing to the old stack,
384 // which creates dangling pointers, which may be detected by the
385 // garbage collector.
386 for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
391 for i := int(sel.ncase) - 1; i >= 0; i-- {
392 k = &scases[pollorder[i]]
393 if sglist.releasetime > 0 {
394 k.releasetime = sglist.releasetime
400 if k.kind == _CaseSend {
401 c.sendq.dequeueSudoG(sglist)
403 c.recvq.dequeueSudoG(sglist)
406 sgnext = sglist.waitlink
407 sglist.waitlink = nil
419 gothrow("selectgo: shouldn't happen")
423 print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
426 if cas.kind == _CaseRecv {
427 if cas.receivedp != nil {
428 *cas.receivedp = true
433 if cas.kind == _CaseRecv && cas.elem != nil {
434 raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
435 } else if cas.kind == _CaseSend {
436 raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
444 // can receive from buffer
447 raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
449 raceacquire(chanbuf(c, c.recvx))
450 racerelease(chanbuf(c, c.recvx))
452 if cas.receivedp != nil {
453 *cas.receivedp = true
456 memmove(cas.elem, chanbuf(c, c.recvx), uintptr(c.elemsize))
458 memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
460 if c.recvx == c.dataqsiz {
464 sg = c.sendq.dequeue()
468 if sg.releasetime != 0 {
469 sg.releasetime = cputicks()
478 // can send to buffer
480 raceacquire(chanbuf(c, c.sendx))
481 racerelease(chanbuf(c, c.sendx))
482 raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
484 memmove(chanbuf(c, c.sendx), cas.elem, uintptr(c.elemsize))
486 if c.sendx == c.dataqsiz {
490 sg = c.recvq.dequeue()
494 if sg.releasetime != 0 {
495 sg.releasetime = cputicks()
504 // can receive from sleeping sender (sg)
507 raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
513 print("syncrecv: sel=", sel, " c=", c, "\n")
515 if cas.receivedp != nil {
516 *cas.receivedp = true
519 memmove(cas.elem, sg.elem, uintptr(c.elemsize))
523 gp.param = unsafe.Pointer(sg)
524 if sg.releasetime != 0 {
525 sg.releasetime = cputicks()
531 // read at end of closed channel
533 if cas.receivedp != nil {
534 *cas.receivedp = false
537 memclr(cas.elem, uintptr(c.elemsize))
540 raceacquire(unsafe.Pointer(c))
545 // can send to sleeping receiver (sg)
547 raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
552 print("syncsend: sel=", sel, " c=", c, "\n")
555 memmove(sg.elem, cas.elem, uintptr(c.elemsize))
559 gp.param = unsafe.Pointer(sg)
560 if sg.releasetime != 0 {
561 sg.releasetime = cputicks()
566 if cas.releasetime > 0 {
567 blockevent(cas.releasetime-t0, 2)
569 return cas.pc, cas.so
572 // send on closed channel
574 panic("send on closed channel")
577 func (c *hchan) sortkey() uintptr {
578 // TODO(khr): if we have a moving garbage collector, we'll need to
579 // change this function.
580 return uintptr(unsafe.Pointer(c))
583 // A runtimeSelect is a single case passed to rselect.
584 // This must match ../reflect/value.go:/runtimeSelect
585 type runtimeSelect struct {
587 typ unsafe.Pointer // channel type (not used here)
589 val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
592 // These values must match ../reflect/value.go:/SelectDir.
597 selectSend // case Chan <- Send
598 selectRecv // case <-Chan:
599 selectDefault // default
602 func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
603 // flagNoScan is safe here, because all objects are also referenced from cases.
604 size := selectsize(uintptr(len(cases)))
605 sel := (*_select)(mallocgc(size, nil, flagNoScan))
606 newselect(sel, int64(size), int32(len(cases)))
608 for i := range cases {
612 selectdefaultImpl(sel, uintptr(i), 0)
617 selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
622 selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
626 pc, _ := selectgoImpl(sel)
632 func (q *waitq) dequeueSudoG(s *sudog) {