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
*_select
) bool {
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(unsafe
.Pointer(funcPC(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
) {