runtime: Initialize variable to avoid compiler warning.
[official-gcc.git] / libgo / runtime / chan.c
blob2ef78eb3b6fe43ada799cd1de14391ba1effd8d1
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.
5 #include "runtime.h"
6 #include "arch.h"
7 #include "go-type.h"
8 #include "race.h"
9 #include "malloc.h"
11 typedef struct WaitQ WaitQ;
12 typedef struct SudoG SudoG;
13 typedef struct Select Select;
14 typedef struct Scase Scase;
16 typedef struct __go_type_descriptor Type;
17 typedef struct __go_channel_type ChanType;
19 struct SudoG
21 G* g;
22 uint32* selectdone;
23 SudoG* link;
24 int64 releasetime;
25 byte* elem; // data element
28 struct WaitQ
30 SudoG* first;
31 SudoG* last;
34 // The garbage collector is assuming that Hchan can only contain pointers into the stack
35 // and cannot contain pointers into the heap.
36 struct Hchan
38 uintgo qcount; // total data in the q
39 uintgo dataqsiz; // size of the circular q
40 uint16 elemsize;
41 uint8 elemalign;
42 uint8 pad; // ensures proper alignment of the buffer that follows Hchan in memory
43 bool closed;
44 const Type* elemtype; // element type
45 uintgo sendx; // send index
46 uintgo recvx; // receive index
47 WaitQ recvq; // list of recv waiters
48 WaitQ sendq; // list of send waiters
49 Lock;
52 uint32 runtime_Hchansize = sizeof(Hchan);
54 // Buffer follows Hchan immediately in memory.
55 // chanbuf(c, i) is pointer to the i'th slot in the buffer.
56 #define chanbuf(c, i) ((byte*)((c)+1)+(uintptr)(c)->elemsize*(i))
58 enum
60 debug = 0,
62 // Scase.kind
63 CaseRecv,
64 CaseSend,
65 CaseDefault,
68 struct Scase
70 SudoG sg; // must be first member (cast to Scase)
71 Hchan* chan; // chan
72 uint16 kind;
73 uint16 index; // index to return
74 bool* receivedp; // pointer to received bool (recv2)
77 struct Select
79 uint16 tcase; // total count of scase[]
80 uint16 ncase; // currently filled scase[]
81 uint16* pollorder; // case poll order
82 Hchan** lockorder; // channel lock order
83 Scase scase[1]; // one per case (in order of appearance)
86 static void dequeueg(WaitQ*);
87 static SudoG* dequeue(WaitQ*);
88 static void enqueue(WaitQ*, SudoG*);
89 static void racesync(Hchan*, SudoG*);
91 static Hchan*
92 makechan(ChanType *t, int64 hint)
94 Hchan *c;
95 uintptr n;
96 const Type *elem;
98 elem = t->__element_type;
100 // compiler checks this but be safe.
101 if(elem->__size >= (1<<16))
102 runtime_throw("makechan: invalid channel element type");
104 if(hint < 0 || (intgo)hint != hint || (elem->__size > 0 && (uintptr)hint > (MaxMem - sizeof(*c)) / elem->__size))
105 runtime_panicstring("makechan: size out of range");
107 n = sizeof(*c);
108 n = ROUND(n, elem->__align);
110 // allocate memory in one call
111 c = (Hchan*)runtime_mallocgc(sizeof(*c) + hint*elem->__size, (uintptr)t | TypeInfo_Chan, 0);
112 c->elemsize = elem->__size;
113 c->elemtype = elem;
114 c->dataqsiz = hint;
116 if(debug)
117 runtime_printf("makechan: chan=%p; elemsize=%D; dataqsiz=%D\n",
118 c, (int64)elem->__size, (int64)c->dataqsiz);
120 return c;
123 // For reflect
124 // func makechan(typ *ChanType, size uint64) (chan)
125 Hchan *reflect_makechan(ChanType *, uint64)
126 __asm__ (GOSYM_PREFIX "reflect.makechan");
128 Hchan *
129 reflect_makechan(ChanType *t, uint64 size)
131 Hchan *c;
133 c = makechan(t, size);
134 return c;
137 // makechan(t *ChanType, hint int64) (hchan *chan any);
138 Hchan*
139 __go_new_channel(ChanType *t, uintptr hint)
141 return makechan(t, hint);
144 Hchan*
145 __go_new_channel_big(ChanType *t, uint64 hint)
147 return makechan(t, hint);
151 * generic single channel send/recv
152 * if the bool pointer is nil,
153 * then the full exchange will
154 * occur. if pres is not nil,
155 * then the protocol will not
156 * sleep but return if it could
157 * not complete.
159 * sleep can wake up with g->param == nil
160 * when a channel involved in the sleep has
161 * been closed. it is easiest to loop and re-run
162 * the operation; we'll see that it's now closed.
164 static bool
165 chansend(ChanType *t, Hchan *c, byte *ep, bool block, void *pc)
167 SudoG *sg;
168 SudoG mysg;
169 G* gp;
170 int64 t0;
171 G* g;
173 g = runtime_g();
175 if(raceenabled)
176 runtime_racereadobjectpc(ep, t->__element_type, runtime_getcallerpc(&t), chansend);
178 if(c == nil) {
179 USED(t);
180 if(!block)
181 return false;
182 runtime_park(nil, nil, "chan send (nil chan)");
183 return false; // not reached
186 if(runtime_gcwaiting())
187 runtime_gosched();
189 if(debug) {
190 runtime_printf("chansend: chan=%p\n", c);
193 t0 = 0;
194 mysg.releasetime = 0;
195 if(runtime_blockprofilerate > 0) {
196 t0 = runtime_cputicks();
197 mysg.releasetime = -1;
200 runtime_lock(c);
201 if(raceenabled)
202 runtime_racereadpc(c, pc, chansend);
203 if(c->closed)
204 goto closed;
206 if(c->dataqsiz > 0)
207 goto asynch;
209 sg = dequeue(&c->recvq);
210 if(sg != nil) {
211 if(raceenabled)
212 racesync(c, sg);
213 runtime_unlock(c);
215 gp = sg->g;
216 gp->param = sg;
217 if(sg->elem != nil)
218 runtime_memmove(sg->elem, ep, c->elemsize);
219 if(sg->releasetime)
220 sg->releasetime = runtime_cputicks();
221 runtime_ready(gp);
222 return true;
225 if(!block) {
226 runtime_unlock(c);
227 return false;
230 mysg.elem = ep;
231 mysg.g = g;
232 mysg.selectdone = nil;
233 g->param = nil;
234 enqueue(&c->sendq, &mysg);
235 runtime_parkunlock(c, "chan send");
237 if(g->param == nil) {
238 runtime_lock(c);
239 if(!c->closed)
240 runtime_throw("chansend: spurious wakeup");
241 goto closed;
244 if(mysg.releasetime > 0)
245 runtime_blockevent(mysg.releasetime - t0, 2);
247 return true;
249 asynch:
250 if(c->closed)
251 goto closed;
253 if(c->qcount >= c->dataqsiz) {
254 if(!block) {
255 runtime_unlock(c);
256 return false;
258 mysg.g = g;
259 mysg.elem = nil;
260 mysg.selectdone = nil;
261 enqueue(&c->sendq, &mysg);
262 runtime_parkunlock(c, "chan send");
264 runtime_lock(c);
265 goto asynch;
268 if(raceenabled)
269 runtime_racerelease(chanbuf(c, c->sendx));
271 runtime_memmove(chanbuf(c, c->sendx), ep, c->elemsize);
272 if(++c->sendx == c->dataqsiz)
273 c->sendx = 0;
274 c->qcount++;
276 sg = dequeue(&c->recvq);
277 if(sg != nil) {
278 gp = sg->g;
279 runtime_unlock(c);
280 if(sg->releasetime)
281 sg->releasetime = runtime_cputicks();
282 runtime_ready(gp);
283 } else
284 runtime_unlock(c);
285 if(mysg.releasetime > 0)
286 runtime_blockevent(mysg.releasetime - t0, 2);
287 return true;
289 closed:
290 runtime_unlock(c);
291 runtime_panicstring("send on closed channel");
292 return false; // not reached
296 static bool
297 chanrecv(ChanType *t, Hchan* c, byte *ep, bool block, bool *received)
299 SudoG *sg;
300 SudoG mysg;
301 G *gp;
302 int64 t0;
303 G *g;
305 if(runtime_gcwaiting())
306 runtime_gosched();
308 // raceenabled: don't need to check ep, as it is always on the stack.
310 if(debug)
311 runtime_printf("chanrecv: chan=%p\n", c);
313 g = runtime_g();
315 if(c == nil) {
316 USED(t);
317 if(!block)
318 return false;
319 runtime_park(nil, nil, "chan receive (nil chan)");
320 return false; // not reached
323 t0 = 0;
324 mysg.releasetime = 0;
325 if(runtime_blockprofilerate > 0) {
326 t0 = runtime_cputicks();
327 mysg.releasetime = -1;
330 runtime_lock(c);
331 if(c->dataqsiz > 0)
332 goto asynch;
334 if(c->closed)
335 goto closed;
337 sg = dequeue(&c->sendq);
338 if(sg != nil) {
339 if(raceenabled)
340 racesync(c, sg);
341 runtime_unlock(c);
343 if(ep != nil)
344 runtime_memmove(ep, sg->elem, c->elemsize);
345 gp = sg->g;
346 gp->param = sg;
347 if(sg->releasetime)
348 sg->releasetime = runtime_cputicks();
349 runtime_ready(gp);
351 if(received != nil)
352 *received = true;
353 return true;
356 if(!block) {
357 runtime_unlock(c);
358 return false;
361 mysg.elem = ep;
362 mysg.g = g;
363 mysg.selectdone = nil;
364 g->param = nil;
365 enqueue(&c->recvq, &mysg);
366 runtime_parkunlock(c, "chan receive");
368 if(g->param == nil) {
369 runtime_lock(c);
370 if(!c->closed)
371 runtime_throw("chanrecv: spurious wakeup");
372 goto closed;
375 if(received != nil)
376 *received = true;
377 if(mysg.releasetime > 0)
378 runtime_blockevent(mysg.releasetime - t0, 2);
379 return true;
381 asynch:
382 if(c->qcount <= 0) {
383 if(c->closed)
384 goto closed;
386 if(!block) {
387 runtime_unlock(c);
388 if(received != nil)
389 *received = false;
390 return false;
392 mysg.g = g;
393 mysg.elem = nil;
394 mysg.selectdone = nil;
395 enqueue(&c->recvq, &mysg);
396 runtime_parkunlock(c, "chan receive");
398 runtime_lock(c);
399 goto asynch;
402 if(raceenabled)
403 runtime_raceacquire(chanbuf(c, c->recvx));
405 if(ep != nil)
406 runtime_memmove(ep, chanbuf(c, c->recvx), c->elemsize);
407 runtime_memclr(chanbuf(c, c->recvx), c->elemsize);
408 if(++c->recvx == c->dataqsiz)
409 c->recvx = 0;
410 c->qcount--;
412 sg = dequeue(&c->sendq);
413 if(sg != nil) {
414 gp = sg->g;
415 runtime_unlock(c);
416 if(sg->releasetime)
417 sg->releasetime = runtime_cputicks();
418 runtime_ready(gp);
419 } else
420 runtime_unlock(c);
422 if(received != nil)
423 *received = true;
424 if(mysg.releasetime > 0)
425 runtime_blockevent(mysg.releasetime - t0, 2);
426 return true;
428 closed:
429 if(ep != nil)
430 runtime_memclr(ep, c->elemsize);
431 if(received != nil)
432 *received = false;
433 if(raceenabled)
434 runtime_raceacquire(c);
435 runtime_unlock(c);
436 if(mysg.releasetime > 0)
437 runtime_blockevent(mysg.releasetime - t0, 2);
438 return true;
441 // The compiler generates a call to __go_send_small to send a value 8
442 // bytes or smaller.
443 void
444 __go_send_small(ChanType *t, Hchan* c, uint64 val)
446 union
448 byte b[sizeof(uint64)];
449 uint64 v;
450 } u;
451 byte *v;
453 u.v = val;
454 #ifndef WORDS_BIGENDIAN
455 v = u.b;
456 #else
457 v = u.b + sizeof(uint64) - t->__element_type->__size;
458 #endif
459 chansend(t, c, v, true, runtime_getcallerpc(&t));
462 // The compiler generates a call to __go_send_big to send a value
463 // larger than 8 bytes or smaller.
464 void
465 __go_send_big(ChanType *t, Hchan* c, byte* v)
467 chansend(t, c, v, true, runtime_getcallerpc(&t));
470 // The compiler generates a call to __go_receive to receive a
471 // value from a channel.
472 void
473 __go_receive(ChanType *t, Hchan* c, byte* v)
475 chanrecv(t, c, v, true, nil);
478 _Bool runtime_chanrecv2(ChanType *t, Hchan* c, byte* v)
479 __asm__ (GOSYM_PREFIX "runtime.chanrecv2");
481 _Bool
482 runtime_chanrecv2(ChanType *t, Hchan* c, byte* v)
484 bool received = false;
486 chanrecv(t, c, v, true, &received);
487 return received;
490 // func selectnbsend(c chan any, elem *any) bool
492 // compiler implements
494 // select {
495 // case c <- v:
496 // ... foo
497 // default:
498 // ... bar
499 // }
501 // as
503 // if selectnbsend(c, v) {
504 // ... foo
505 // } else {
506 // ... bar
507 // }
509 _Bool
510 runtime_selectnbsend(ChanType *t, Hchan *c, byte *val)
512 bool res;
514 res = chansend(t, c, val, false, runtime_getcallerpc(&t));
515 return (_Bool)res;
518 // func selectnbrecv(elem *any, c chan any) bool
520 // compiler implements
522 // select {
523 // case v = <-c:
524 // ... foo
525 // default:
526 // ... bar
527 // }
529 // as
531 // if selectnbrecv(&v, c) {
532 // ... foo
533 // } else {
534 // ... bar
535 // }
537 _Bool
538 runtime_selectnbrecv(ChanType *t, byte *v, Hchan *c)
540 bool selected;
542 selected = chanrecv(t, c, v, false, nil);
543 return (_Bool)selected;
546 // func selectnbrecv2(elem *any, ok *bool, c chan any) bool
548 // compiler implements
550 // select {
551 // case v, ok = <-c:
552 // ... foo
553 // default:
554 // ... bar
555 // }
557 // as
559 // if c != nil && selectnbrecv2(&v, &ok, c) {
560 // ... foo
561 // } else {
562 // ... bar
563 // }
565 _Bool
566 runtime_selectnbrecv2(ChanType *t, byte *v, _Bool *received, Hchan *c)
568 bool selected;
569 bool r;
571 r = false;
572 selected = chanrecv(t, c, v, false, received == nil ? nil : &r);
573 if(received != nil)
574 *received = r;
575 return selected;
578 // For reflect:
579 // func chansend(c chan, val *any, nb bool) (selected bool)
580 // where val points to the data to be sent.
582 // The "uintptr selected" is really "bool selected" but saying
583 // uintptr gets us the right alignment for the output parameter block.
585 _Bool reflect_chansend(ChanType *, Hchan *, byte *, _Bool)
586 __asm__ (GOSYM_PREFIX "reflect.chansend");
588 _Bool
589 reflect_chansend(ChanType *t, Hchan *c, byte *val, _Bool nb)
591 bool selected;
593 selected = chansend(t, c, val, !nb, runtime_getcallerpc(&t));
594 return (_Bool)selected;
597 // For reflect:
598 // func chanrecv(c chan, nb bool, val *any) (selected, received bool)
599 // where val points to a data area that will be filled in with the
600 // received value. val must have the size and type of the channel element type.
602 struct chanrecv_ret
604 _Bool selected;
605 _Bool received;
608 struct chanrecv_ret reflect_chanrecv(ChanType *, Hchan *, _Bool, byte *val)
609 __asm__ (GOSYM_PREFIX "reflect.chanrecv");
611 struct chanrecv_ret
612 reflect_chanrecv(ChanType *t, Hchan *c, _Bool nb, byte *val)
614 struct chanrecv_ret ret;
615 bool selected;
616 bool received;
618 received = false;
619 selected = chanrecv(t, c, val, !nb, &received);
620 ret.selected = (_Bool)selected;
621 ret.received = (_Bool)received;
622 return ret;
625 static Select* newselect(int32);
627 // newselect(size uint32) (sel *byte);
629 void* runtime_newselect(int32) __asm__ (GOSYM_PREFIX "runtime.newselect");
631 void*
632 runtime_newselect(int32 size)
634 return (void*)newselect(size);
637 static Select*
638 newselect(int32 size)
640 int32 n;
641 Select *sel;
643 n = 0;
644 if(size > 1)
645 n = size-1;
647 // allocate all the memory we need in a single allocation
648 // start with Select with size cases
649 // then lockorder with size entries
650 // then pollorder with size entries
651 sel = runtime_mal(sizeof(*sel) +
652 n*sizeof(sel->scase[0]) +
653 size*sizeof(sel->lockorder[0]) +
654 size*sizeof(sel->pollorder[0]));
656 sel->tcase = size;
657 sel->ncase = 0;
658 sel->lockorder = (void*)(sel->scase + size);
659 sel->pollorder = (void*)(sel->lockorder + size);
661 if(debug)
662 runtime_printf("newselect s=%p size=%d\n", sel, size);
663 return sel;
666 // cut in half to give stack a chance to split
667 static void selectsend(Select *sel, Hchan *c, int index, void *elem);
669 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
671 void runtime_selectsend(Select *, Hchan *, void *, int32)
672 __asm__ (GOSYM_PREFIX "runtime.selectsend");
674 void
675 runtime_selectsend(Select *sel, Hchan *c, void *elem, int32 index)
677 // nil cases do not compete
678 if(c == nil)
679 return;
681 selectsend(sel, c, index, elem);
684 static void
685 selectsend(Select *sel, Hchan *c, int index, void *elem)
687 int32 i;
688 Scase *cas;
690 i = sel->ncase;
691 if(i >= sel->tcase)
692 runtime_throw("selectsend: too many cases");
693 sel->ncase = i+1;
694 cas = &sel->scase[i];
696 cas->index = index;
697 cas->chan = c;
698 cas->kind = CaseSend;
699 cas->sg.elem = elem;
701 if(debug)
702 runtime_printf("selectsend s=%p index=%d chan=%p\n",
703 sel, cas->index, cas->chan);
706 // cut in half to give stack a chance to split
707 static void selectrecv(Select *sel, Hchan *c, int index, void *elem, bool*);
709 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
711 void runtime_selectrecv(Select *, Hchan *, void *, int32)
712 __asm__ (GOSYM_PREFIX "runtime.selectrecv");
714 void
715 runtime_selectrecv(Select *sel, Hchan *c, void *elem, int32 index)
717 // nil cases do not compete
718 if(c == nil)
719 return;
721 selectrecv(sel, c, index, elem, nil);
724 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
726 void runtime_selectrecv2(Select *, Hchan *, void *, bool *, int32)
727 __asm__ (GOSYM_PREFIX "runtime.selectrecv2");
729 void
730 runtime_selectrecv2(Select *sel, Hchan *c, void *elem, bool *received, int32 index)
732 // nil cases do not compete
733 if(c == nil)
734 return;
736 selectrecv(sel, c, index, elem, received);
739 static void
740 selectrecv(Select *sel, Hchan *c, int index, void *elem, bool *received)
742 int32 i;
743 Scase *cas;
745 i = sel->ncase;
746 if(i >= sel->tcase)
747 runtime_throw("selectrecv: too many cases");
748 sel->ncase = i+1;
749 cas = &sel->scase[i];
750 cas->index = index;
751 cas->chan = c;
753 cas->kind = CaseRecv;
754 cas->sg.elem = elem;
755 cas->receivedp = received;
757 if(debug)
758 runtime_printf("selectrecv s=%p index=%d chan=%p\n",
759 sel, cas->index, cas->chan);
762 // cut in half to give stack a chance to split
763 static void selectdefault(Select*, int);
765 // selectdefault(sel *byte) (selected bool);
767 void runtime_selectdefault(Select *, int32) __asm__ (GOSYM_PREFIX "runtime.selectdefault");
769 void
770 runtime_selectdefault(Select *sel, int32 index)
772 selectdefault(sel, index);
775 static void
776 selectdefault(Select *sel, int32 index)
778 int32 i;
779 Scase *cas;
781 i = sel->ncase;
782 if(i >= sel->tcase)
783 runtime_throw("selectdefault: too many cases");
784 sel->ncase = i+1;
785 cas = &sel->scase[i];
786 cas->index = index;
787 cas->chan = nil;
789 cas->kind = CaseDefault;
791 if(debug)
792 runtime_printf("selectdefault s=%p index=%d\n",
793 sel, cas->index);
796 static void
797 sellock(Select *sel)
799 uint32 i;
800 Hchan *c, *c0;
802 c = nil;
803 for(i=0; i<sel->ncase; i++) {
804 c0 = sel->lockorder[i];
805 if(c0 && c0 != c) {
806 c = sel->lockorder[i];
807 runtime_lock(c);
812 static void
813 selunlock(Select *sel)
815 int32 i, n, r;
816 Hchan *c;
818 // We must be very careful here to not touch sel after we have unlocked
819 // the last lock, because sel can be freed right after the last unlock.
820 // Consider the following situation.
821 // First M calls runtime_park() in runtime_selectgo() passing the sel.
822 // Once runtime_park() has unlocked the last lock, another M makes
823 // the G that calls select runnable again and schedules it for execution.
824 // When the G runs on another M, it locks all the locks and frees sel.
825 // Now if the first M touches sel, it will access freed memory.
826 n = (int32)sel->ncase;
827 r = 0;
828 // skip the default case
829 if(n>0 && sel->lockorder[0] == nil)
830 r = 1;
831 for(i = n-1; i >= r; i--) {
832 c = sel->lockorder[i];
833 if(i>0 && sel->lockorder[i-1] == c)
834 continue; // will unlock it on the next iteration
835 runtime_unlock(c);
839 static bool
840 selparkcommit(G *gp, void *sel)
842 USED(gp);
843 selunlock(sel);
844 return true;
847 void
848 runtime_block(void)
850 runtime_park(nil, nil, "select (no cases)"); // forever
853 static int selectgo(Select**);
855 // selectgo(sel *byte);
857 int runtime_selectgo(Select *) __asm__ (GOSYM_PREFIX "runtime.selectgo");
860 runtime_selectgo(Select *sel)
862 return selectgo(&sel);
865 static int
866 selectgo(Select **selp)
868 Select *sel;
869 uint32 o, i, j, k, done;
870 int64 t0;
871 Scase *cas, *dfl;
872 Hchan *c;
873 SudoG *sg;
874 G *gp;
875 int index;
876 G *g;
878 sel = *selp;
879 if(runtime_gcwaiting())
880 runtime_gosched();
882 if(debug)
883 runtime_printf("select: sel=%p\n", sel);
885 g = runtime_g();
887 t0 = 0;
888 if(runtime_blockprofilerate > 0) {
889 t0 = runtime_cputicks();
890 for(i=0; i<sel->ncase; i++)
891 sel->scase[i].sg.releasetime = -1;
894 // The compiler rewrites selects that statically have
895 // only 0 or 1 cases plus default into simpler constructs.
896 // The only way we can end up with such small sel->ncase
897 // values here is for a larger select in which most channels
898 // have been nilled out. The general code handles those
899 // cases correctly, and they are rare enough not to bother
900 // optimizing (and needing to test).
902 // generate permuted order
903 for(i=0; i<sel->ncase; i++)
904 sel->pollorder[i] = i;
905 for(i=1; i<sel->ncase; i++) {
906 o = sel->pollorder[i];
907 j = runtime_fastrand1()%(i+1);
908 sel->pollorder[i] = sel->pollorder[j];
909 sel->pollorder[j] = o;
912 // sort the cases by Hchan address to get the locking order.
913 // simple heap sort, to guarantee n log n time and constant stack footprint.
914 for(i=0; i<sel->ncase; i++) {
915 j = i;
916 c = sel->scase[j].chan;
917 while(j > 0 && sel->lockorder[k=(j-1)/2] < c) {
918 sel->lockorder[j] = sel->lockorder[k];
919 j = k;
921 sel->lockorder[j] = c;
923 for(i=sel->ncase; i-->0; ) {
924 c = sel->lockorder[i];
925 sel->lockorder[i] = sel->lockorder[0];
926 j = 0;
927 for(;;) {
928 k = j*2+1;
929 if(k >= i)
930 break;
931 if(k+1 < i && sel->lockorder[k] < sel->lockorder[k+1])
932 k++;
933 if(c < sel->lockorder[k]) {
934 sel->lockorder[j] = sel->lockorder[k];
935 j = k;
936 continue;
938 break;
940 sel->lockorder[j] = c;
943 for(i=0; i+1<sel->ncase; i++)
944 if(sel->lockorder[i] > sel->lockorder[i+1]) {
945 runtime_printf("i=%d %p %p\n", i, sel->lockorder[i], sel->lockorder[i+1]);
946 runtime_throw("select: broken sort");
949 sellock(sel);
951 loop:
952 // pass 1 - look for something already waiting
953 dfl = nil;
954 for(i=0; i<sel->ncase; i++) {
955 o = sel->pollorder[i];
956 cas = &sel->scase[o];
957 c = cas->chan;
959 switch(cas->kind) {
960 case CaseRecv:
961 if(c->dataqsiz > 0) {
962 if(c->qcount > 0)
963 goto asyncrecv;
964 } else {
965 sg = dequeue(&c->sendq);
966 if(sg != nil)
967 goto syncrecv;
969 if(c->closed)
970 goto rclose;
971 break;
973 case CaseSend:
974 if(raceenabled)
975 runtime_racereadpc(c, runtime_selectgo, chansend);
976 if(c->closed)
977 goto sclose;
978 if(c->dataqsiz > 0) {
979 if(c->qcount < c->dataqsiz)
980 goto asyncsend;
981 } else {
982 sg = dequeue(&c->recvq);
983 if(sg != nil)
984 goto syncsend;
986 break;
988 case CaseDefault:
989 dfl = cas;
990 break;
994 if(dfl != nil) {
995 selunlock(sel);
996 cas = dfl;
997 goto retc;
1001 // pass 2 - enqueue on all chans
1002 done = 0;
1003 for(i=0; i<sel->ncase; i++) {
1004 o = sel->pollorder[i];
1005 cas = &sel->scase[o];
1006 c = cas->chan;
1007 sg = &cas->sg;
1008 sg->g = g;
1009 sg->selectdone = &done;
1011 switch(cas->kind) {
1012 case CaseRecv:
1013 enqueue(&c->recvq, sg);
1014 break;
1016 case CaseSend:
1017 enqueue(&c->sendq, sg);
1018 break;
1022 g->param = nil;
1023 runtime_park(selparkcommit, sel, "select");
1025 sellock(sel);
1026 sg = g->param;
1028 // pass 3 - dequeue from unsuccessful chans
1029 // otherwise they stack up on quiet channels
1030 for(i=0; i<sel->ncase; i++) {
1031 cas = &sel->scase[i];
1032 if(cas != (Scase*)sg) {
1033 c = cas->chan;
1034 if(cas->kind == CaseSend)
1035 dequeueg(&c->sendq);
1036 else
1037 dequeueg(&c->recvq);
1041 if(sg == nil)
1042 goto loop;
1044 cas = (Scase*)sg;
1045 c = cas->chan;
1047 if(c->dataqsiz > 0)
1048 runtime_throw("selectgo: shouldn't happen");
1050 if(debug)
1051 runtime_printf("wait-return: sel=%p c=%p cas=%p kind=%d\n",
1052 sel, c, cas, cas->kind);
1054 if(cas->kind == CaseRecv) {
1055 if(cas->receivedp != nil)
1056 *cas->receivedp = true;
1059 if(raceenabled) {
1060 if(cas->kind == CaseRecv && cas->sg.elem != nil)
1061 runtime_racewriteobjectpc(cas->sg.elem, c->elemtype, selectgo, chanrecv);
1062 else if(cas->kind == CaseSend)
1063 runtime_racereadobjectpc(cas->sg.elem, c->elemtype, selectgo, chansend);
1066 selunlock(sel);
1067 goto retc;
1069 asyncrecv:
1070 // can receive from buffer
1071 if(raceenabled) {
1072 if(cas->sg.elem != nil)
1073 runtime_racewriteobjectpc(cas->sg.elem, c->elemtype, selectgo, chanrecv);
1074 runtime_raceacquire(chanbuf(c, c->recvx));
1076 if(cas->receivedp != nil)
1077 *cas->receivedp = true;
1078 if(cas->sg.elem != nil)
1079 runtime_memmove(cas->sg.elem, chanbuf(c, c->recvx), c->elemsize);
1080 runtime_memclr(chanbuf(c, c->recvx), c->elemsize);
1081 if(++c->recvx == c->dataqsiz)
1082 c->recvx = 0;
1083 c->qcount--;
1084 sg = dequeue(&c->sendq);
1085 if(sg != nil) {
1086 gp = sg->g;
1087 selunlock(sel);
1088 if(sg->releasetime)
1089 sg->releasetime = runtime_cputicks();
1090 runtime_ready(gp);
1091 } else {
1092 selunlock(sel);
1094 goto retc;
1096 asyncsend:
1097 // can send to buffer
1098 if(raceenabled) {
1099 runtime_racerelease(chanbuf(c, c->sendx));
1100 runtime_racereadobjectpc(cas->sg.elem, c->elemtype, selectgo, chansend);
1102 runtime_memmove(chanbuf(c, c->sendx), cas->sg.elem, c->elemsize);
1103 if(++c->sendx == c->dataqsiz)
1104 c->sendx = 0;
1105 c->qcount++;
1106 sg = dequeue(&c->recvq);
1107 if(sg != nil) {
1108 gp = sg->g;
1109 selunlock(sel);
1110 if(sg->releasetime)
1111 sg->releasetime = runtime_cputicks();
1112 runtime_ready(gp);
1113 } else {
1114 selunlock(sel);
1116 goto retc;
1118 syncrecv:
1119 // can receive from sleeping sender (sg)
1120 if(raceenabled) {
1121 if(cas->sg.elem != nil)
1122 runtime_racewriteobjectpc(cas->sg.elem, c->elemtype, selectgo, chanrecv);
1123 racesync(c, sg);
1125 selunlock(sel);
1126 if(debug)
1127 runtime_printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
1128 if(cas->receivedp != nil)
1129 *cas->receivedp = true;
1130 if(cas->sg.elem != nil)
1131 runtime_memmove(cas->sg.elem, sg->elem, c->elemsize);
1132 gp = sg->g;
1133 gp->param = sg;
1134 if(sg->releasetime)
1135 sg->releasetime = runtime_cputicks();
1136 runtime_ready(gp);
1137 goto retc;
1139 rclose:
1140 // read at end of closed channel
1141 selunlock(sel);
1142 if(cas->receivedp != nil)
1143 *cas->receivedp = false;
1144 if(cas->sg.elem != nil)
1145 runtime_memclr(cas->sg.elem, c->elemsize);
1146 if(raceenabled)
1147 runtime_raceacquire(c);
1148 goto retc;
1150 syncsend:
1151 // can send to sleeping receiver (sg)
1152 if(raceenabled) {
1153 runtime_racereadobjectpc(cas->sg.elem, c->elemtype, selectgo, chansend);
1154 racesync(c, sg);
1156 selunlock(sel);
1157 if(debug)
1158 runtime_printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
1159 if(sg->elem != nil)
1160 runtime_memmove(sg->elem, cas->sg.elem, c->elemsize);
1161 gp = sg->g;
1162 gp->param = sg;
1163 if(sg->releasetime)
1164 sg->releasetime = runtime_cputicks();
1165 runtime_ready(gp);
1167 retc:
1168 // return index corresponding to chosen case
1169 index = cas->index;
1170 if(cas->sg.releasetime > 0)
1171 runtime_blockevent(cas->sg.releasetime - t0, 2);
1172 runtime_free(sel);
1173 return index;
1175 sclose:
1176 // send on closed channel
1177 selunlock(sel);
1178 runtime_panicstring("send on closed channel");
1179 return 0; // not reached
1182 // This struct must match ../reflect/value.go:/runtimeSelect.
1183 typedef struct runtimeSelect runtimeSelect;
1184 struct runtimeSelect
1186 uintptr dir;
1187 ChanType *typ;
1188 Hchan *ch;
1189 byte *val;
1192 // This enum must match ../reflect/value.go:/SelectDir.
1193 enum SelectDir {
1194 SelectSend = 1,
1195 SelectRecv,
1196 SelectDefault,
1199 // func rselect(cases []runtimeSelect) (chosen int, recvOK bool)
1201 struct rselect_ret {
1202 intgo chosen;
1203 _Bool recvOK;
1206 struct rselect_ret reflect_rselect(Slice)
1207 __asm__ (GOSYM_PREFIX "reflect.rselect");
1209 struct rselect_ret
1210 reflect_rselect(Slice cases)
1212 struct rselect_ret ret;
1213 intgo chosen;
1214 bool recvOK;
1215 int32 i;
1216 Select *sel;
1217 runtimeSelect* rcase, *rc;
1219 chosen = -1;
1220 recvOK = false;
1222 rcase = (runtimeSelect*)cases.__values;
1224 sel = newselect(cases.__count);
1225 for(i=0; i<cases.__count; i++) {
1226 rc = &rcase[i];
1227 switch(rc->dir) {
1228 case SelectDefault:
1229 selectdefault(sel, i);
1230 break;
1231 case SelectSend:
1232 if(rc->ch == nil)
1233 break;
1234 selectsend(sel, rc->ch, i, rc->val);
1235 break;
1236 case SelectRecv:
1237 if(rc->ch == nil)
1238 break;
1239 selectrecv(sel, rc->ch, i, rc->val, &recvOK);
1240 break;
1244 chosen = (intgo)(uintptr)selectgo(&sel);
1246 ret.chosen = chosen;
1247 ret.recvOK = (_Bool)recvOK;
1248 return ret;
1251 static void closechan(Hchan *c, void *pc);
1253 // closechan(sel *byte);
1254 void
1255 runtime_closechan(Hchan *c)
1257 closechan(c, runtime_getcallerpc(&c));
1260 // For reflect
1261 // func chanclose(c chan)
1263 void reflect_chanclose(Hchan *) __asm__ (GOSYM_PREFIX "reflect.chanclose");
1265 void
1266 reflect_chanclose(Hchan *c)
1268 closechan(c, runtime_getcallerpc(&c));
1271 static void
1272 closechan(Hchan *c, void *pc)
1274 SudoG *sg;
1275 G* gp;
1277 if(c == nil)
1278 runtime_panicstring("close of nil channel");
1280 if(runtime_gcwaiting())
1281 runtime_gosched();
1283 runtime_lock(c);
1284 if(c->closed) {
1285 runtime_unlock(c);
1286 runtime_panicstring("close of closed channel");
1289 if(raceenabled) {
1290 runtime_racewritepc(c, pc, runtime_closechan);
1291 runtime_racerelease(c);
1294 c->closed = true;
1296 // release all readers
1297 for(;;) {
1298 sg = dequeue(&c->recvq);
1299 if(sg == nil)
1300 break;
1301 gp = sg->g;
1302 gp->param = nil;
1303 if(sg->releasetime)
1304 sg->releasetime = runtime_cputicks();
1305 runtime_ready(gp);
1308 // release all writers
1309 for(;;) {
1310 sg = dequeue(&c->sendq);
1311 if(sg == nil)
1312 break;
1313 gp = sg->g;
1314 gp->param = nil;
1315 if(sg->releasetime)
1316 sg->releasetime = runtime_cputicks();
1317 runtime_ready(gp);
1320 runtime_unlock(c);
1323 void
1324 __go_builtin_close(Hchan *c)
1326 runtime_closechan(c);
1329 // For reflect
1330 // func chanlen(c chan) (len int)
1332 intgo reflect_chanlen(Hchan *) __asm__ (GOSYM_PREFIX "reflect.chanlen");
1334 intgo
1335 reflect_chanlen(Hchan *c)
1337 intgo len;
1339 if(c == nil)
1340 len = 0;
1341 else
1342 len = c->qcount;
1343 return len;
1346 intgo
1347 __go_chan_len(Hchan *c)
1349 return reflect_chanlen(c);
1352 // For reflect
1353 // func chancap(c chan) int
1355 intgo reflect_chancap(Hchan *) __asm__ (GOSYM_PREFIX "reflect.chancap");
1357 intgo
1358 reflect_chancap(Hchan *c)
1360 intgo cap;
1362 if(c == nil)
1363 cap = 0;
1364 else
1365 cap = c->dataqsiz;
1366 return cap;
1369 intgo
1370 __go_chan_cap(Hchan *c)
1372 return reflect_chancap(c);
1375 static SudoG*
1376 dequeue(WaitQ *q)
1378 SudoG *sgp;
1380 loop:
1381 sgp = q->first;
1382 if(sgp == nil)
1383 return nil;
1384 q->first = sgp->link;
1386 // if sgp participates in a select and is already signaled, ignore it
1387 if(sgp->selectdone != nil) {
1388 // claim the right to signal
1389 if(*sgp->selectdone != 0 || !runtime_cas(sgp->selectdone, 0, 1))
1390 goto loop;
1393 return sgp;
1396 static void
1397 dequeueg(WaitQ *q)
1399 SudoG **l, *sgp, *prevsgp;
1400 G *g;
1402 g = runtime_g();
1403 prevsgp = nil;
1404 for(l=&q->first; (sgp=*l) != nil; l=&sgp->link, prevsgp=sgp) {
1405 if(sgp->g == g) {
1406 *l = sgp->link;
1407 if(q->last == sgp)
1408 q->last = prevsgp;
1409 break;
1414 static void
1415 enqueue(WaitQ *q, SudoG *sgp)
1417 sgp->link = nil;
1418 if(q->first == nil) {
1419 q->first = sgp;
1420 q->last = sgp;
1421 return;
1423 q->last->link = sgp;
1424 q->last = sgp;
1427 static void
1428 racesync(Hchan *c, SudoG *sg)
1430 runtime_racerelease(chanbuf(c, 0));
1431 runtime_raceacquireg(sg->g, chanbuf(c, 0));
1432 runtime_racereleaseg(sg->g, chanbuf(c, 0));
1433 runtime_raceacquire(chanbuf(c, 0));