blob: 20dd2995b667b92a582eefd873b487bae362cfe9 [file] [log] [blame]
Keith Randall1d8fa7f2014-09-02 14:13:29 -07001// 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
5package runtime
6
7// This file contains the implementation of Go select statements.
8
9import "unsafe"
10
11const (
12 debugSelect = false
13)
14
15var (
Russ Cox0e07f1c2014-09-03 11:10:38 -040016 chansendpc = funcPC(chansend)
17 chanrecvpc = funcPC(chanrecv)
Keith Randall1d8fa7f2014-09-02 14:13:29 -070018)
19
Keith Randall1d8fa7f2014-09-02 14:13:29 -070020func 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)
26}
27
28func 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")
Keith Randallb2a950b2014-12-27 20:58:00 -080031 throw("bad select size")
Keith Randall1d8fa7f2014-09-02 14:13:29 -070032 }
33 sel.tcase = uint16(size)
34 sel.ncase = 0
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)))
37
38 if debugSelect {
39 print("newselect s=", sel, " size=", size, "\n")
40 }
41}
42
43//go:nosplit
44func selectsend(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) {
45 // nil cases do not compete
46 if c != nil {
47 selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
48 }
49 return
50}
51
52// cut in half to give stack a chance to split
53func selectsendImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) {
54 i := sel.ncase
55 if i >= sel.tcase {
Keith Randallb2a950b2014-12-27 20:58:00 -080056 throw("selectsend: too many cases")
Keith Randall1d8fa7f2014-09-02 14:13:29 -070057 }
58 sel.ncase = i + 1
59 cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
60
61 cas.pc = pc
62 cas._chan = c
63 cas.so = uint16(so)
64 cas.kind = _CaseSend
65 cas.elem = elem
66
67 if debugSelect {
68 print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n")
69 }
70}
71
72//go:nosplit
73func selectrecv(sel *_select, c *hchan, elem unsafe.Pointer) (selected bool) {
74 // nil cases do not compete
75 if c != nil {
76 selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
77 }
78 return
79}
80
81//go:nosplit
82func selectrecv2(sel *_select, c *hchan, elem unsafe.Pointer, received *bool) (selected bool) {
83 // nil cases do not compete
84 if c != nil {
85 selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
86 }
87 return
88}
89
90func selectrecvImpl(sel *_select, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, so uintptr) {
91 i := sel.ncase
92 if i >= sel.tcase {
Keith Randallb2a950b2014-12-27 20:58:00 -080093 throw("selectrecv: too many cases")
Keith Randall1d8fa7f2014-09-02 14:13:29 -070094 }
95 sel.ncase = i + 1
96 cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
97 cas.pc = pc
98 cas._chan = c
99 cas.so = uint16(so)
100 cas.kind = _CaseRecv
101 cas.elem = elem
102 cas.receivedp = received
103
104 if debugSelect {
105 print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas._chan, " so=", cas.so, "\n")
106 }
107}
108
109//go:nosplit
110func selectdefault(sel *_select) (selected bool) {
111 selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
112 return
113}
114
115func selectdefaultImpl(sel *_select, callerpc uintptr, so uintptr) {
116 i := sel.ncase
117 if i >= sel.tcase {
Keith Randallb2a950b2014-12-27 20:58:00 -0800118 throw("selectdefault: too many cases")
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700119 }
120 sel.ncase = i + 1
121 cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
122 cas.pc = callerpc
123 cas._chan = nil
124 cas.so = uint16(so)
125 cas.kind = _CaseDefault
126
127 if debugSelect {
128 print("selectdefault s=", sel, " pc=", hex(cas.pc), " so=", cas.so, "\n")
129 }
130}
131
132func sellock(sel *_select) {
133 lockslice := sliceStruct{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
134 lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
135 var c *hchan
136 for _, c0 := range lockorder {
137 if c0 != nil && c0 != c {
138 c = c0
139 lock(&c.lock)
140 }
141 }
142}
143
144func 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.
153 n := int(sel.ncase)
154 r := 0
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 {
159 r = 1
160 }
161 for i := n - 1; i >= r; i-- {
162 c := lockorder[i]
163 if i > 0 && c == lockorder[i-1] {
164 continue // will unlock it on the next iteration
165 }
166 unlock(&c.lock)
167 }
168}
169
Russ Coxb2cdf302014-11-11 17:08:33 -0500170func selparkcommit(gp *g, sel unsafe.Pointer) bool {
171 selunlock((*_select)(sel))
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700172 return true
173}
174
175func block() {
176 gopark(nil, nil, "select (no cases)") // forever
177}
178
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.
181//go:nosplit
182func selectgo(sel *_select) {
183 pc, offset := selectgoImpl(sel)
184 *(*bool)(add(unsafe.Pointer(&sel), uintptr(offset))) = true
185 setcallerpc(unsafe.Pointer(&sel), pc)
186}
187
188// selectgoImpl returns scase.pc and scase.so for the select
189// case which fired.
190func selectgoImpl(sel *_select) (uintptr, uint16) {
191 if debugSelect {
192 print("select: sel=", sel, "\n")
193 }
194
195 scaseslice := sliceStruct{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
196 scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
197
198 var t0 int64
199 if blockprofilerate > 0 {
200 t0 = cputicks()
201 for i := 0; i < int(sel.ncase); i++ {
202 scases[i].releasetime = -1
203 }
204 }
205
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).
213
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)
219 }
220 for i := 1; i < int(sel.ncase); i++ {
221 o := pollorder[i]
Keith Randall3306d112014-09-02 14:33:33 -0700222 j := int(fastrand1()) % (i + 1)
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700223 pollorder[i] = pollorder[j]
224 pollorder[j] = o
225 }
226
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++ {
232 j := i
233 c := scases[j]._chan
234 for j > 0 && lockorder[(j-1)/2].sortkey() < c.sortkey() {
235 k := (j - 1) / 2
236 lockorder[j] = lockorder[k]
237 j = k
238 }
239 lockorder[j] = c
240 }
241 for i := int(sel.ncase) - 1; i >= 0; i-- {
242 c := lockorder[i]
243 lockorder[i] = lockorder[0]
244 j := 0
245 for {
246 k := j*2 + 1
247 if k >= i {
248 break
249 }
250 if k+1 < i && lockorder[k].sortkey() < lockorder[k+1].sortkey() {
251 k++
252 }
253 if c.sortkey() < lockorder[k].sortkey() {
254 lockorder[j] = lockorder[k]
255 j = k
256 continue
257 }
258 break
259 }
260 lockorder[j] = c
261 }
262 /*
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")
Keith Randallb2a950b2014-12-27 20:58:00 -0800266 throw("select: broken sort")
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700267 }
268 }
269 */
270
271 // lock all the channels involved in the select
272 sellock(sel)
273
274 var (
275 gp *g
276 done uint32
277 sg *sudog
278 c *hchan
279 k *scase
280 sglist *sudog
281 sgnext *sudog
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700282 )
283
284loop:
285 // pass 1 - look for something already waiting
286 var dfl *scase
287 var cas *scase
288 for i := 0; i < int(sel.ncase); i++ {
289 cas = &scases[pollorder[i]]
290 c = cas._chan
291
292 switch cas.kind {
293 case _CaseRecv:
294 if c.dataqsiz > 0 {
295 if c.qcount > 0 {
296 goto asyncrecv
297 }
298 } else {
299 sg = c.sendq.dequeue()
300 if sg != nil {
301 goto syncrecv
302 }
303 }
304 if c.closed != 0 {
305 goto rclose
306 }
307
308 case _CaseSend:
309 if raceenabled {
310 racereadpc(unsafe.Pointer(c), cas.pc, chansendpc)
311 }
312 if c.closed != 0 {
313 goto sclose
314 }
315 if c.dataqsiz > 0 {
316 if c.qcount < c.dataqsiz {
317 goto asyncsend
318 }
319 } else {
320 sg = c.recvq.dequeue()
321 if sg != nil {
322 goto syncsend
323 }
324 }
325
326 case _CaseDefault:
327 dfl = cas
328 }
329 }
330
331 if dfl != nil {
332 selunlock(sel)
333 cas = dfl
334 goto retc
335 }
336
337 // pass 2 - enqueue on all chans
338 gp = getg()
339 done = 0
340 for i := 0; i < int(sel.ncase); i++ {
341 cas = &scases[pollorder[i]]
342 c = cas._chan
343 sg := acquireSudog()
344 sg.g = gp
345 // Note: selectdone is adjusted for stack copies in stack.c:adjustsudogs
346 sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done)))
347 sg.elem = cas.elem
348 sg.releasetime = 0
349 if t0 != 0 {
350 sg.releasetime = -1
351 }
352 sg.waitlink = gp.waiting
353 gp.waiting = sg
354
355 switch cas.kind {
356 case _CaseRecv:
357 c.recvq.enqueue(sg)
358
359 case _CaseSend:
360 c.sendq.enqueue(sg)
361 }
362 }
363
364 // wait for someone to wake us up
365 gp.param = nil
Russ Coxb2cdf302014-11-11 17:08:33 -0500366 gopark(selparkcommit, unsafe.Pointer(sel), "select")
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700367
368 // someone woke us up
369 sellock(sel)
370 sg = (*sudog)(gp.param)
Russ Coxa3630c92014-10-02 16:49:11 -0400371 gp.param = nil
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700372
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.
378 cas = nil
379 sglist = gp.waiting
Russ Coxa3630c92014-10-02 16:49:11 -0400380 // Clear all elem before unlinking from gp.waiting.
381 for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
Russ Cox13da3602014-10-03 15:33:29 -0400382 sg1.selectdone = nil
Russ Coxa3630c92014-10-02 16:49:11 -0400383 sg1.elem = nil
384 }
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700385 gp.waiting = nil
386 for i := int(sel.ncase) - 1; i >= 0; i-- {
387 k = &scases[pollorder[i]]
388 if sglist.releasetime > 0 {
389 k.releasetime = sglist.releasetime
390 }
391 if sg == sglist {
Keith Randall8eb8b402014-12-08 10:11:08 -0800392 // sg has already been dequeued by the G that woke us up.
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700393 cas = k
394 } else {
395 c = k._chan
396 if k.kind == _CaseSend {
Keith Randalle330cc12014-10-18 21:02:49 -0700397 c.sendq.dequeueSudoG(sglist)
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700398 } else {
Keith Randalle330cc12014-10-18 21:02:49 -0700399 c.recvq.dequeueSudoG(sglist)
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700400 }
401 }
402 sgnext = sglist.waitlink
Russ Coxb3932ba2014-11-16 16:44:45 -0500403 sglist.waitlink = nil
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700404 releaseSudog(sglist)
405 sglist = sgnext
406 }
407
408 if cas == nil {
409 goto loop
410 }
411
412 c = cas._chan
413
414 if c.dataqsiz > 0 {
Keith Randallb2a950b2014-12-27 20:58:00 -0800415 throw("selectgo: shouldn't happen")
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700416 }
417
418 if debugSelect {
419 print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
420 }
421
422 if cas.kind == _CaseRecv {
423 if cas.receivedp != nil {
424 *cas.receivedp = true
425 }
426 }
427
428 if raceenabled {
429 if cas.kind == _CaseRecv && cas.elem != nil {
430 raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
431 } else if cas.kind == _CaseSend {
432 raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
433 }
434 }
435
436 selunlock(sel)
437 goto retc
438
439asyncrecv:
440 // can receive from buffer
441 if raceenabled {
442 if cas.elem != nil {
443 raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
444 }
445 raceacquire(chanbuf(c, c.recvx))
446 racerelease(chanbuf(c, c.recvx))
447 }
448 if cas.receivedp != nil {
449 *cas.receivedp = true
450 }
451 if cas.elem != nil {
Russ Cox54bb4dc2014-12-29 10:07:47 -0500452 typedmemmove(c.elemtype, cas.elem, chanbuf(c, c.recvx))
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700453 }
454 memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
455 c.recvx++
456 if c.recvx == c.dataqsiz {
457 c.recvx = 0
458 }
459 c.qcount--
460 sg = c.sendq.dequeue()
461 if sg != nil {
462 gp = sg.g
463 selunlock(sel)
464 if sg.releasetime != 0 {
465 sg.releasetime = cputicks()
466 }
467 goready(gp)
468 } else {
469 selunlock(sel)
470 }
471 goto retc
472
473asyncsend:
474 // can send to buffer
475 if raceenabled {
476 raceacquire(chanbuf(c, c.sendx))
477 racerelease(chanbuf(c, c.sendx))
478 raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
479 }
Russ Cox54bb4dc2014-12-29 10:07:47 -0500480 typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem)
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700481 c.sendx++
482 if c.sendx == c.dataqsiz {
483 c.sendx = 0
484 }
485 c.qcount++
486 sg = c.recvq.dequeue()
487 if sg != nil {
488 gp = sg.g
489 selunlock(sel)
490 if sg.releasetime != 0 {
491 sg.releasetime = cputicks()
492 }
493 goready(gp)
494 } else {
495 selunlock(sel)
496 }
497 goto retc
498
499syncrecv:
500 // can receive from sleeping sender (sg)
501 if raceenabled {
502 if cas.elem != nil {
503 raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
504 }
505 racesync(c, sg)
506 }
507 selunlock(sel)
508 if debugSelect {
509 print("syncrecv: sel=", sel, " c=", c, "\n")
510 }
511 if cas.receivedp != nil {
512 *cas.receivedp = true
513 }
514 if cas.elem != nil {
Russ Cox54bb4dc2014-12-29 10:07:47 -0500515 typedmemmove(c.elemtype, cas.elem, sg.elem)
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700516 }
Russ Coxa3630c92014-10-02 16:49:11 -0400517 sg.elem = nil
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700518 gp = sg.g
519 gp.param = unsafe.Pointer(sg)
520 if sg.releasetime != 0 {
521 sg.releasetime = cputicks()
522 }
523 goready(gp)
524 goto retc
525
526rclose:
527 // read at end of closed channel
528 selunlock(sel)
529 if cas.receivedp != nil {
530 *cas.receivedp = false
531 }
532 if cas.elem != nil {
533 memclr(cas.elem, uintptr(c.elemsize))
534 }
535 if raceenabled {
536 raceacquire(unsafe.Pointer(c))
537 }
538 goto retc
539
540syncsend:
541 // can send to sleeping receiver (sg)
542 if raceenabled {
543 raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
544 racesync(c, sg)
545 }
546 selunlock(sel)
547 if debugSelect {
548 print("syncsend: sel=", sel, " c=", c, "\n")
549 }
550 if sg.elem != nil {
Russ Cox54bb4dc2014-12-29 10:07:47 -0500551 typedmemmove(c.elemtype, sg.elem, cas.elem)
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700552 }
Russ Coxa3630c92014-10-02 16:49:11 -0400553 sg.elem = nil
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700554 gp = sg.g
555 gp.param = unsafe.Pointer(sg)
556 if sg.releasetime != 0 {
557 sg.releasetime = cputicks()
558 }
559 goready(gp)
560
561retc:
562 if cas.releasetime > 0 {
563 blockevent(cas.releasetime-t0, 2)
564 }
565 return cas.pc, cas.so
566
567sclose:
568 // send on closed channel
569 selunlock(sel)
570 panic("send on closed channel")
571}
572
573func (c *hchan) sortkey() uintptr {
574 // TODO(khr): if we have a moving garbage collector, we'll need to
575 // change this function.
576 return uintptr(unsafe.Pointer(c))
577}
578
579// A runtimeSelect is a single case passed to rselect.
580// This must match ../reflect/value.go:/runtimeSelect
581type runtimeSelect struct {
582 dir selectDir
583 typ unsafe.Pointer // channel type (not used here)
584 ch *hchan // channel
585 val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
586}
587
588// These values must match ../reflect/value.go:/SelectDir.
589type selectDir int
590
591const (
592 _ selectDir = iota
593 selectSend // case Chan <- Send
594 selectRecv // case <-Chan:
595 selectDefault // default
596)
597
Russ Cox7a524a12014-12-22 13:27:53 -0500598//go:linkname reflect_rselect reflect.rselect
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700599func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
600 // flagNoScan is safe here, because all objects are also referenced from cases.
601 size := selectsize(uintptr(len(cases)))
Russ Coxbffb0592014-09-09 01:08:34 -0400602 sel := (*_select)(mallocgc(size, nil, flagNoScan))
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700603 newselect(sel, int64(size), int32(len(cases)))
604 r := new(bool)
605 for i := range cases {
606 rc := &cases[i]
607 switch rc.dir {
608 case selectDefault:
609 selectdefaultImpl(sel, uintptr(i), 0)
610 case selectSend:
611 if rc.ch == nil {
612 break
613 }
614 selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
615 case selectRecv:
616 if rc.ch == nil {
617 break
618 }
619 selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
620 }
621 }
622
623 pc, _ := selectgoImpl(sel)
624 chosen = int(pc)
625 recvOK = *r
626 return
627}
628
Keith Randall8eb8b402014-12-08 10:11:08 -0800629func (q *waitq) dequeueSudoG(sgp *sudog) {
630 x := sgp.prev
631 y := sgp.next
632 if x != nil {
633 if y != nil {
634 // middle of queue
635 x.next = y
636 y.prev = x
637 sgp.next = nil
638 sgp.prev = nil
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700639 return
640 }
Keith Randall8eb8b402014-12-08 10:11:08 -0800641 // end of queue
642 x.next = nil
643 q.last = x
644 sgp.prev = nil
645 return
646 }
647 if y != nil {
648 // start of queue
649 y.prev = nil
650 q.first = y
651 sgp.next = nil
652 return
653 }
654
655 // x==y==nil. Either sgp is the only element in the queue,
656 // or it has already been removed. Use q.first to disambiguate.
657 if q.first == sgp {
658 q.first = nil
659 q.last = nil
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700660 }
661}