github.com/xrash/gopher-lua@v0.0.0-20160304065408-e5faab4db06a/channellib.go (about) 1 package lua 2 3 import ( 4 "reflect" 5 ) 6 7 func checkChannel(L *LState, idx int) reflect.Value { 8 ch := L.CheckChannel(idx) 9 return reflect.ValueOf(ch) 10 } 11 12 func checkGoroutineSafe(L *LState, idx int) LValue { 13 v := L.CheckAny(2) 14 if !isGoroutineSafe(v) { 15 L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable") 16 } 17 return v 18 } 19 20 func OpenChannel(L *LState) int { 21 var mod LValue 22 //_, ok := L.G.builtinMts[int(LTChannel)] 23 // if !ok { 24 mod = L.RegisterModule(ChannelLibName, channelFuncs) 25 mt := L.SetFuncs(L.NewTable(), channelMethods) 26 mt.RawSetString("__index", mt) 27 L.G.builtinMts[int(LTChannel)] = mt 28 // } 29 L.Push(mod) 30 return 1 31 } 32 33 var channelFuncs = map[string]LGFunction{ 34 "make": channelMake, 35 "select": channelSelect, 36 } 37 38 func channelMake(L *LState) int { 39 buffer := L.OptInt(1, 0) 40 L.Push(LChannel(make(chan LValue, buffer))) 41 return 1 42 } 43 44 func channelSelect(L *LState) int { 45 //TODO check case table size 46 cases := make([]reflect.SelectCase, L.GetTop()) 47 top := L.GetTop() 48 for i := 0; i < top; i++ { 49 cas := reflect.SelectCase{reflect.SelectSend, reflect.ValueOf(nil), reflect.ValueOf(nil)} 50 tbl := L.CheckTable(i + 1) 51 dir, ok1 := tbl.RawGetInt(1).(LString) 52 if !ok1 { 53 L.ArgError(i+1, "invalid select case") 54 } 55 switch string(dir) { 56 case "<-|": 57 ch, ok := tbl.RawGetInt(2).(LChannel) 58 if !ok { 59 L.ArgError(i+1, "invalid select case") 60 } 61 cas.Chan = reflect.ValueOf((chan LValue)(ch)) 62 v := tbl.RawGetInt(3) 63 if !isGoroutineSafe(v) { 64 L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable") 65 } 66 cas.Send = reflect.ValueOf(v) 67 case "|<-": 68 ch, ok := tbl.RawGetInt(2).(LChannel) 69 if !ok { 70 L.ArgError(i+1, "invalid select case") 71 } 72 cas.Chan = reflect.ValueOf((chan LValue)(ch)) 73 cas.Dir = reflect.SelectRecv 74 case "default": 75 cas.Dir = reflect.SelectDefault 76 default: 77 L.ArgError(i+1, "invalid channel direction:"+string(dir)) 78 } 79 cases[i] = cas 80 } 81 82 pos, recv, rok := reflect.Select(cases) 83 lv := LNil 84 if recv.Kind() != 0 { 85 lv, _ = recv.Interface().(LValue) 86 if lv == nil { 87 lv = LNil 88 } 89 } 90 tbl := L.Get(pos + 1).(*LTable) 91 last := tbl.RawGetInt(tbl.Len()) 92 if last.Type() == LTFunction { 93 L.Push(last) 94 switch cases[pos].Dir { 95 case reflect.SelectRecv: 96 if rok { 97 L.Push(LTrue) 98 } else { 99 L.Push(LFalse) 100 } 101 L.Push(lv) 102 L.Call(2, 0) 103 case reflect.SelectSend: 104 L.Push(tbl.RawGetInt(3)) 105 L.Call(1, 0) 106 case reflect.SelectDefault: 107 L.Call(0, 0) 108 } 109 } 110 L.Push(LNumber(pos + 1)) 111 L.Push(lv) 112 if rok { 113 L.Push(LTrue) 114 } else { 115 L.Push(LFalse) 116 } 117 return 3 118 } 119 120 var channelMethods = map[string]LGFunction{ 121 "receive": channelReceive, 122 "send": channelSend, 123 "close": channelClose, 124 } 125 126 func channelReceive(L *LState) int { 127 rch := checkChannel(L, 1) 128 v, ok := rch.Recv() 129 if ok { 130 L.Push(LTrue) 131 L.Push(v.Interface().(LValue)) 132 } else { 133 L.Push(LFalse) 134 L.Push(LNil) 135 } 136 return 2 137 } 138 139 func channelSend(L *LState) int { 140 rch := checkChannel(L, 1) 141 v := checkGoroutineSafe(L, 2) 142 rch.Send(reflect.ValueOf(v)) 143 return 0 144 } 145 146 func channelClose(L *LState) int { 147 rch := checkChannel(L, 1) 148 rch.Close() 149 return 0 150 } 151 152 //