Home | History | Annotate | Download | only in runtime
      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.
      4 
      5 package runtime
      6 
      7 // This file contains the implementation of Go select statements.
      8 
      9 import "unsafe"
     10 
     11 const (
     12 	debugSelect = false
     13 
     14 	// scase.kind
     15 	caseRecv = iota
     16 	caseSend
     17 	caseDefault
     18 )
     19 
     20 // Select statement header.
     21 // Known to compiler.
     22 // Changes here must also be made in src/cmd/internal/gc/select.go's selecttype.
     23 type hselect struct {
     24 	tcase     uint16   // total count of scase[]
     25 	ncase     uint16   // currently filled scase[]
     26 	pollorder *uint16  // case poll order
     27 	lockorder **hchan  // channel lock order
     28 	scase     [1]scase // one per case (in order of appearance)
     29 }
     30 
     31 // Select case descriptor.
     32 // Known to compiler.
     33 // Changes here must also be made in src/cmd/internal/gc/select.go's selecttype.
     34 type scase struct {
     35 	elem        unsafe.Pointer // data element
     36 	c           *hchan         // chan
     37 	pc          uintptr        // return pc
     38 	kind        uint16
     39 	so          uint16 // vararg of selected bool
     40 	receivedp   *bool  // pointer to received bool (recv2)
     41 	releasetime int64
     42 }
     43 
     44 var (
     45 	chansendpc = funcPC(chansend)
     46 	chanrecvpc = funcPC(chanrecv)
     47 )
     48 
     49 func selectsize(size uintptr) uintptr {
     50 	selsize := unsafe.Sizeof(hselect{}) +
     51 		(size-1)*unsafe.Sizeof(hselect{}.scase[0]) +
     52 		size*unsafe.Sizeof(*hselect{}.lockorder) +
     53 		size*unsafe.Sizeof(*hselect{}.pollorder)
     54 	return round(selsize, _Int64Align)
     55 }
     56 
     57 func newselect(sel *hselect, selsize int64, size int32) {
     58 	if selsize != int64(selectsize(uintptr(size))) {
     59 		print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n")
     60 		throw("bad select size")
     61 	}
     62 	sel.tcase = uint16(size)
     63 	sel.ncase = 0
     64 	sel.lockorder = (**hchan)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(hselect{}.scase[0])))
     65 	sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*hselect{}.lockorder)))
     66 
     67 	if debugSelect {
     68 		print("newselect s=", sel, " size=", size, "\n")
     69 	}
     70 }
     71 
     72 //go:nosplit
     73 func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer) (selected bool) {
     74 	// nil cases do not compete
     75 	if c != nil {
     76 		selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
     77 	}
     78 	return
     79 }
     80 
     81 // cut in half to give stack a chance to split
     82 func selectsendImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) {
     83 	i := sel.ncase
     84 	if i >= sel.tcase {
     85 		throw("selectsend: too many cases")
     86 	}
     87 	sel.ncase = i + 1
     88 	cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
     89 
     90 	cas.pc = pc
     91 	cas.c = c
     92 	cas.so = uint16(so)
     93 	cas.kind = caseSend
     94 	cas.elem = elem
     95 
     96 	if debugSelect {
     97 		print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " so=", cas.so, "\n")
     98 	}
     99 }
    100 
    101 //go:nosplit
    102 func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer) (selected bool) {
    103 	// nil cases do not compete
    104 	if c != nil {
    105 		selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
    106 	}
    107 	return
    108 }
    109 
    110 //go:nosplit
    111 func selectrecv2(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool) (selected bool) {
    112 	// nil cases do not compete
    113 	if c != nil {
    114 		selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
    115 	}
    116 	return
    117 }
    118 
    119 func selectrecvImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, so uintptr) {
    120 	i := sel.ncase
    121 	if i >= sel.tcase {
    122 		throw("selectrecv: too many cases")
    123 	}
    124 	sel.ncase = i + 1
    125 	cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
    126 	cas.pc = pc
    127 	cas.c = c
    128 	cas.so = uint16(so)
    129 	cas.kind = caseRecv
    130 	cas.elem = elem
    131 	cas.receivedp = received
    132 
    133 	if debugSelect {
    134 		print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " so=", cas.so, "\n")
    135 	}
    136 }
    137 
    138 //go:nosplit
    139 func selectdefault(sel *hselect) (selected bool) {
    140 	selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
    141 	return
    142 }
    143 
    144 func selectdefaultImpl(sel *hselect, callerpc uintptr, so uintptr) {
    145 	i := sel.ncase
    146 	if i >= sel.tcase {
    147 		throw("selectdefault: too many cases")
    148 	}
    149 	sel.ncase = i + 1
    150 	cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
    151 	cas.pc = callerpc
    152 	cas.c = nil
    153 	cas.so = uint16(so)
    154 	cas.kind = caseDefault
    155 
    156 	if debugSelect {
    157 		print("selectdefault s=", sel, " pc=", hex(cas.pc), " so=", cas.so, "\n")
    158 	}
    159 }
    160 
    161 func sellock(sel *hselect) {
    162 	lockslice := slice{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
    163 	lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
    164 	var c *hchan
    165 	for _, c0 := range lockorder {
    166 		if c0 != nil && c0 != c {
    167 			c = c0
    168 			lock(&c.lock)
    169 		}
    170 	}
    171 }
    172 
    173 func selunlock(sel *hselect) {
    174 	// We must be very careful here to not touch sel after we have unlocked
    175 	// the last lock, because sel can be freed right after the last unlock.
    176 	// Consider the following situation.
    177 	// First M calls runtimepark() in runtimeselectgo() passing the sel.
    178 	// Once runtimepark() has unlocked the last lock, another M makes
    179 	// the G that calls select runnable again and schedules it for execution.
    180 	// When the G runs on another M, it locks all the locks and frees sel.
    181 	// Now if the first M touches sel, it will access freed memory.
    182 	n := int(sel.ncase)
    183 	r := 0
    184 	lockslice := slice{unsafe.Pointer(sel.lockorder), n, n}
    185 	lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
    186 	// skip the default case
    187 	if n > 0 && lockorder[0] == nil {
    188 		r = 1
    189 	}
    190 	for i := n - 1; i >= r; i-- {
    191 		c := lockorder[i]
    192 		if i > 0 && c == lockorder[i-1] {
    193 			continue // will unlock it on the next iteration
    194 		}
    195 		unlock(&c.lock)
    196 	}
    197 }
    198 
    199 func selparkcommit(gp *g, sel unsafe.Pointer) bool {
    200 	selunlock((*hselect)(sel))
    201 	return true
    202 }
    203 
    204 func block() {
    205 	gopark(nil, nil, "select (no cases)", traceEvGoStop, 1) // forever
    206 }
    207 
    208 // overwrites return pc on stack to signal which case of the select
    209 // to run, so cannot appear at the top of a split stack.
    210 //go:nosplit
    211 func selectgo(sel *hselect) {
    212 	pc, offset := selectgoImpl(sel)
    213 	*(*bool)(add(unsafe.Pointer(&sel), uintptr(offset))) = true
    214 	setcallerpc(unsafe.Pointer(&sel), pc)
    215 }
    216 
    217 // selectgoImpl returns scase.pc and scase.so for the select
    218 // case which fired.
    219 func selectgoImpl(sel *hselect) (uintptr, uint16) {
    220 	if debugSelect {
    221 		print("select: sel=", sel, "\n")
    222 	}
    223 
    224 	scaseslice := slice{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
    225 	scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
    226 
    227 	var t0 int64
    228 	if blockprofilerate > 0 {
    229 		t0 = cputicks()
    230 		for i := 0; i < int(sel.ncase); i++ {
    231 			scases[i].releasetime = -1
    232 		}
    233 	}
    234 
    235 	// The compiler rewrites selects that statically have
    236 	// only 0 or 1 cases plus default into simpler constructs.
    237 	// The only way we can end up with such small sel.ncase
    238 	// values here is for a larger select in which most channels
    239 	// have been nilled out.  The general code handles those
    240 	// cases correctly, and they are rare enough not to bother
    241 	// optimizing (and needing to test).
    242 
    243 	// generate permuted order
    244 	pollslice := slice{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)}
    245 	pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice))
    246 	for i := 1; i < int(sel.ncase); i++ {
    247 		j := int(fastrand1()) % (i + 1)
    248 		pollorder[i] = pollorder[j]
    249 		pollorder[j] = uint16(i)
    250 	}
    251 
    252 	// sort the cases by Hchan address to get the locking order.
    253 	// simple heap sort, to guarantee n log n time and constant stack footprint.
    254 	lockslice := slice{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
    255 	lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
    256 	for i := 0; i < int(sel.ncase); i++ {
    257 		j := i
    258 		c := scases[j].c
    259 		for j > 0 && lockorder[(j-1)/2].sortkey() < c.sortkey() {
    260 			k := (j - 1) / 2
    261 			lockorder[j] = lockorder[k]
    262 			j = k
    263 		}
    264 		lockorder[j] = c
    265 	}
    266 	for i := int(sel.ncase) - 1; i >= 0; i-- {
    267 		c := lockorder[i]
    268 		lockorder[i] = lockorder[0]
    269 		j := 0
    270 		for {
    271 			k := j*2 + 1
    272 			if k >= i {
    273 				break
    274 			}
    275 			if k+1 < i && lockorder[k].sortkey() < lockorder[k+1].sortkey() {
    276 				k++
    277 			}
    278 			if c.sortkey() < lockorder[k].sortkey() {
    279 				lockorder[j] = lockorder[k]
    280 				j = k
    281 				continue
    282 			}
    283 			break
    284 		}
    285 		lockorder[j] = c
    286 	}
    287 	/*
    288 		for i := 0; i+1 < int(sel.ncase); i++ {
    289 			if lockorder[i].sortkey() > lockorder[i+1].sortkey() {
    290 				print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n")
    291 				throw("select: broken sort")
    292 			}
    293 		}
    294 	*/
    295 
    296 	// lock all the channels involved in the select
    297 	sellock(sel)
    298 
    299 	var (
    300 		gp     *g
    301 		done   uint32
    302 		sg     *sudog
    303 		c      *hchan
    304 		k      *scase
    305 		sglist *sudog
    306 		sgnext *sudog
    307 		futile byte
    308 	)
    309 
    310 loop:
    311 	// pass 1 - look for something already waiting
    312 	var dfl *scase
    313 	var cas *scase
    314 	for i := 0; i < int(sel.ncase); i++ {
    315 		cas = &scases[pollorder[i]]
    316 		c = cas.c
    317 
    318 		switch cas.kind {
    319 		case caseRecv:
    320 			if c.dataqsiz > 0 {
    321 				if c.qcount > 0 {
    322 					goto asyncrecv
    323 				}
    324 			} else {
    325 				sg = c.sendq.dequeue()
    326 				if sg != nil {
    327 					goto syncrecv
    328 				}
    329 			}
    330 			if c.closed != 0 {
    331 				goto rclose
    332 			}
    333 
    334 		case caseSend:
    335 			if raceenabled {
    336 				racereadpc(unsafe.Pointer(c), cas.pc, chansendpc)
    337 			}
    338 			if c.closed != 0 {
    339 				goto sclose
    340 			}
    341 			if c.dataqsiz > 0 {
    342 				if c.qcount < c.dataqsiz {
    343 					goto asyncsend
    344 				}
    345 			} else {
    346 				sg = c.recvq.dequeue()
    347 				if sg != nil {
    348 					goto syncsend
    349 				}
    350 			}
    351 
    352 		case caseDefault:
    353 			dfl = cas
    354 		}
    355 	}
    356 
    357 	if dfl != nil {
    358 		selunlock(sel)
    359 		cas = dfl
    360 		goto retc
    361 	}
    362 
    363 	// pass 2 - enqueue on all chans
    364 	gp = getg()
    365 	done = 0
    366 	for i := 0; i < int(sel.ncase); i++ {
    367 		cas = &scases[pollorder[i]]
    368 		c = cas.c
    369 		sg := acquireSudog()
    370 		sg.g = gp
    371 		// Note: selectdone is adjusted for stack copies in stack1.go:adjustsudogs
    372 		sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done)))
    373 		sg.elem = cas.elem
    374 		sg.releasetime = 0
    375 		if t0 != 0 {
    376 			sg.releasetime = -1
    377 		}
    378 		sg.waitlink = gp.waiting
    379 		gp.waiting = sg
    380 
    381 		switch cas.kind {
    382 		case caseRecv:
    383 			c.recvq.enqueue(sg)
    384 
    385 		case caseSend:
    386 			c.sendq.enqueue(sg)
    387 		}
    388 	}
    389 
    390 	// wait for someone to wake us up
    391 	gp.param = nil
    392 	gopark(selparkcommit, unsafe.Pointer(sel), "select", traceEvGoBlockSelect|futile, 2)
    393 
    394 	// someone woke us up
    395 	sellock(sel)
    396 	sg = (*sudog)(gp.param)
    397 	gp.param = nil
    398 
    399 	// pass 3 - dequeue from unsuccessful chans
    400 	// otherwise they stack up on quiet channels
    401 	// record the successful case, if any.
    402 	// We singly-linked up the SudoGs in case order, so when
    403 	// iterating through the linked list they are in reverse order.
    404 	cas = nil
    405 	sglist = gp.waiting
    406 	// Clear all elem before unlinking from gp.waiting.
    407 	for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
    408 		sg1.selectdone = nil
    409 		sg1.elem = nil
    410 	}
    411 	gp.waiting = nil
    412 	for i := int(sel.ncase) - 1; i >= 0; i-- {
    413 		k = &scases[pollorder[i]]
    414 		if sglist.releasetime > 0 {
    415 			k.releasetime = sglist.releasetime
    416 		}
    417 		if sg == sglist {
    418 			// sg has already been dequeued by the G that woke us up.
    419 			cas = k
    420 		} else {
    421 			c = k.c
    422 			if k.kind == caseSend {
    423 				c.sendq.dequeueSudoG(sglist)
    424 			} else {
    425 				c.recvq.dequeueSudoG(sglist)
    426 			}
    427 		}
    428 		sgnext = sglist.waitlink
    429 		sglist.waitlink = nil
    430 		releaseSudog(sglist)
    431 		sglist = sgnext
    432 	}
    433 
    434 	if cas == nil {
    435 		futile = traceFutileWakeup
    436 		goto loop
    437 	}
    438 
    439 	c = cas.c
    440 
    441 	if c.dataqsiz > 0 {
    442 		throw("selectgo: shouldn't happen")
    443 	}
    444 
    445 	if debugSelect {
    446 		print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
    447 	}
    448 
    449 	if cas.kind == caseRecv {
    450 		if cas.receivedp != nil {
    451 			*cas.receivedp = true
    452 		}
    453 	}
    454 
    455 	if raceenabled {
    456 		if cas.kind == caseRecv && cas.elem != nil {
    457 			raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
    458 		} else if cas.kind == caseSend {
    459 			raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
    460 		}
    461 	}
    462 
    463 	selunlock(sel)
    464 	goto retc
    465 
    466 asyncrecv:
    467 	// can receive from buffer
    468 	if raceenabled {
    469 		if cas.elem != nil {
    470 			raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
    471 		}
    472 		raceacquire(chanbuf(c, c.recvx))
    473 		racerelease(chanbuf(c, c.recvx))
    474 	}
    475 	if cas.receivedp != nil {
    476 		*cas.receivedp = true
    477 	}
    478 	if cas.elem != nil {
    479 		typedmemmove(c.elemtype, cas.elem, chanbuf(c, c.recvx))
    480 	}
    481 	memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
    482 	c.recvx++
    483 	if c.recvx == c.dataqsiz {
    484 		c.recvx = 0
    485 	}
    486 	c.qcount--
    487 	sg = c.sendq.dequeue()
    488 	if sg != nil {
    489 		gp = sg.g
    490 		selunlock(sel)
    491 		if sg.releasetime != 0 {
    492 			sg.releasetime = cputicks()
    493 		}
    494 		goready(gp, 3)
    495 	} else {
    496 		selunlock(sel)
    497 	}
    498 	goto retc
    499 
    500 asyncsend:
    501 	// can send to buffer
    502 	if raceenabled {
    503 		raceacquire(chanbuf(c, c.sendx))
    504 		racerelease(chanbuf(c, c.sendx))
    505 		raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
    506 	}
    507 	typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem)
    508 	c.sendx++
    509 	if c.sendx == c.dataqsiz {
    510 		c.sendx = 0
    511 	}
    512 	c.qcount++
    513 	sg = c.recvq.dequeue()
    514 	if sg != nil {
    515 		gp = sg.g
    516 		selunlock(sel)
    517 		if sg.releasetime != 0 {
    518 			sg.releasetime = cputicks()
    519 		}
    520 		goready(gp, 3)
    521 	} else {
    522 		selunlock(sel)
    523 	}
    524 	goto retc
    525 
    526 syncrecv:
    527 	// can receive from sleeping sender (sg)
    528 	if raceenabled {
    529 		if cas.elem != nil {
    530 			raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
    531 		}
    532 		racesync(c, sg)
    533 	}
    534 	selunlock(sel)
    535 	if debugSelect {
    536 		print("syncrecv: sel=", sel, " c=", c, "\n")
    537 	}
    538 	if cas.receivedp != nil {
    539 		*cas.receivedp = true
    540 	}
    541 	if cas.elem != nil {
    542 		typedmemmove(c.elemtype, cas.elem, sg.elem)
    543 	}
    544 	sg.elem = nil
    545 	gp = sg.g
    546 	gp.param = unsafe.Pointer(sg)
    547 	if sg.releasetime != 0 {
    548 		sg.releasetime = cputicks()
    549 	}
    550 	goready(gp, 3)
    551 	goto retc
    552 
    553 rclose:
    554 	// read at end of closed channel
    555 	selunlock(sel)
    556 	if cas.receivedp != nil {
    557 		*cas.receivedp = false
    558 	}
    559 	if cas.elem != nil {
    560 		memclr(cas.elem, uintptr(c.elemsize))
    561 	}
    562 	if raceenabled {
    563 		raceacquire(unsafe.Pointer(c))
    564 	}
    565 	goto retc
    566 
    567 syncsend:
    568 	// can send to sleeping receiver (sg)
    569 	if raceenabled {
    570 		raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
    571 		racesync(c, sg)
    572 	}
    573 	selunlock(sel)
    574 	if debugSelect {
    575 		print("syncsend: sel=", sel, " c=", c, "\n")
    576 	}
    577 	if sg.elem != nil {
    578 		syncsend(c, sg, cas.elem)
    579 	}
    580 	sg.elem = nil
    581 	gp = sg.g
    582 	gp.param = unsafe.Pointer(sg)
    583 	if sg.releasetime != 0 {
    584 		sg.releasetime = cputicks()
    585 	}
    586 	goready(gp, 3)
    587 
    588 retc:
    589 	if cas.releasetime > 0 {
    590 		blockevent(cas.releasetime-t0, 2)
    591 	}
    592 	return cas.pc, cas.so
    593 
    594 sclose:
    595 	// send on closed channel
    596 	selunlock(sel)
    597 	panic("send on closed channel")
    598 }
    599 
    600 func (c *hchan) sortkey() uintptr {
    601 	// TODO(khr): if we have a moving garbage collector, we'll need to
    602 	// change this function.
    603 	return uintptr(unsafe.Pointer(c))
    604 }
    605 
    606 // A runtimeSelect is a single case passed to rselect.
    607 // This must match ../reflect/value.go:/runtimeSelect
    608 type runtimeSelect struct {
    609 	dir selectDir
    610 	typ unsafe.Pointer // channel type (not used here)
    611 	ch  *hchan         // channel
    612 	val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
    613 }
    614 
    615 // These values must match ../reflect/value.go:/SelectDir.
    616 type selectDir int
    617 
    618 const (
    619 	_             selectDir = iota
    620 	selectSend              // case Chan <- Send
    621 	selectRecv              // case <-Chan:
    622 	selectDefault           // default
    623 )
    624 
    625 //go:linkname reflect_rselect reflect.rselect
    626 func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
    627 	// flagNoScan is safe here, because all objects are also referenced from cases.
    628 	size := selectsize(uintptr(len(cases)))
    629 	sel := (*hselect)(mallocgc(size, nil, flagNoScan))
    630 	newselect(sel, int64(size), int32(len(cases)))
    631 	r := new(bool)
    632 	for i := range cases {
    633 		rc := &cases[i]
    634 		switch rc.dir {
    635 		case selectDefault:
    636 			selectdefaultImpl(sel, uintptr(i), 0)
    637 		case selectSend:
    638 			if rc.ch == nil {
    639 				break
    640 			}
    641 			selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
    642 		case selectRecv:
    643 			if rc.ch == nil {
    644 				break
    645 			}
    646 			selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
    647 		}
    648 	}
    649 
    650 	pc, _ := selectgoImpl(sel)
    651 	chosen = int(pc)
    652 	recvOK = *r
    653 	return
    654 }
    655 
    656 func (q *waitq) dequeueSudoG(sgp *sudog) {
    657 	x := sgp.prev
    658 	y := sgp.next
    659 	if x != nil {
    660 		if y != nil {
    661 			// middle of queue
    662 			x.next = y
    663 			y.prev = x
    664 			sgp.next = nil
    665 			sgp.prev = nil
    666 			return
    667 		}
    668 		// end of queue
    669 		x.next = nil
    670 		q.last = x
    671 		sgp.prev = nil
    672 		return
    673 	}
    674 	if y != nil {
    675 		// start of queue
    676 		y.prev = nil
    677 		q.first = y
    678 		sgp.next = nil
    679 		return
    680 	}
    681 
    682 	// x==y==nil.  Either sgp is the only element in the queue,
    683 	// or it has already been removed.  Use q.first to disambiguate.
    684 	if q.first == sgp {
    685 		q.first = nil
    686 		q.last = nil
    687 	}
    688 }
    689