func loop(rc reflect.Value, wc reflect.Value) { readCase := reflect.SelectCase{} readCase.Dir = reflect.SelectRecv readCase.Chan = wc writeCase := reflect.SelectCase{} writeCase.Dir = reflect.SelectSend writeCase.Chan = rc cases := []reflect.SelectCase{readCase, writeCase} l := list.New() for { tc := cases[:1] if l.Len() > 0 { writeCase.Send = reflect.ValueOf(l.Front().Value) cases[1] = writeCase tc = cases[:2] } idx, v, ok := reflect.Select(tc) if idx == 0 { if ok { l.PushBack(v.Interface()) } else { //channel closed log.Println("Read channel closed") break } } else if idx == 1 { l.Remove(l.Front()) } } }
func channelSelect(L *LState) int { //TODO check case table size cases := make([]reflect.SelectCase, L.GetTop()) top := L.GetTop() for i := 0; i < top; i++ { cas := reflect.SelectCase{reflect.SelectSend, reflect.ValueOf(nil), reflect.ValueOf(nil)} tbl := L.CheckTable(i + 1) dir, ok1 := tbl.RawGetInt(1).(LString) if !ok1 { L.ArgError(i+1, "invalid select case") } switch string(dir) { case "<-|": ch, ok := tbl.RawGetInt(2).(LChannel) if !ok { L.ArgError(i+1, "invalid select case") } cas.Chan = reflect.ValueOf((chan LValue)(ch)) v := tbl.RawGetInt(3) if !isGoroutineSafe(v) { L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable") } cas.Send = reflect.ValueOf(v) case "|<-": ch, ok := tbl.RawGetInt(2).(LChannel) if !ok { L.ArgError(i+1, "invalid select case") } cas.Chan = reflect.ValueOf((chan LValue)(ch)) cas.Dir = reflect.SelectRecv case "default": cas.Dir = reflect.SelectDefault default: L.ArgError(i+1, "invalid channel direction:"+string(dir)) } cases[i] = cas } pos, recv, rok := reflect.Select(cases) lv := LNil if recv.Kind() != 0 { lv, _ = recv.Interface().(LValue) if lv == nil { lv = LNil } } tbl := L.Get(pos + 1).(*LTable) last := tbl.RawGetInt(tbl.Len()) if last.Type() == LTFunction { L.Push(last) switch cases[pos].Dir { case reflect.SelectRecv: if rok { L.Push(LTrue) } else { L.Push(LFalse) } L.Push(lv) L.Call(2, 0) case reflect.SelectSend: L.Push(tbl.RawGetInt(3)) L.Call(1, 0) case reflect.SelectDefault: L.Call(0, 0) } } L.Push(LNumber(pos + 1)) L.Push(lv) if rok { L.Push(LTrue) } else { L.Push(LFalse) } return 3 }