github.com/tul/gopher-lua@v0.0.0-20181008131706-f6fcaab0c612/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{ 50 Dir: reflect.SelectSend, 51 Chan: reflect.ValueOf(nil), 52 Send: reflect.ValueOf(nil), 53 } 54 tbl := L.CheckTable(i + 1) 55 dir, ok1 := tbl.RawGetInt(1).(LString) 56 if !ok1 { 57 L.ArgError(i+1, "invalid select case") 58 } 59 switch string(dir) { 60 case "<-|": 61 ch, ok := tbl.RawGetInt(2).(LChannel) 62 if !ok { 63 L.ArgError(i+1, "invalid select case") 64 } 65 cas.Chan = reflect.ValueOf((chan LValue)(ch)) 66 v := tbl.RawGetInt(3) 67 if !isGoroutineSafe(v) { 68 L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable") 69 } 70 cas.Send = reflect.ValueOf(v) 71 case "|<-": 72 ch, ok := tbl.RawGetInt(2).(LChannel) 73 if !ok { 74 L.ArgError(i+1, "invalid select case") 75 } 76 cas.Chan = reflect.ValueOf((chan LValue)(ch)) 77 cas.Dir = reflect.SelectRecv 78 case "default": 79 cas.Dir = reflect.SelectDefault 80 default: 81 L.ArgError(i+1, "invalid channel direction:"+string(dir)) 82 } 83 cases[i] = cas 84 } 85 86 pos, recv, rok := reflect.Select(cases) 87 lv := LNil 88 if recv.Kind() != 0 { 89 lv, _ = recv.Interface().(LValue) 90 if lv == nil { 91 lv = LNil 92 } 93 } 94 tbl := L.Get(pos + 1).(*LTable) 95 last := tbl.RawGetInt(tbl.Len()) 96 if last.Type() == LTFunction { 97 L.Push(last) 98 switch cases[pos].Dir { 99 case reflect.SelectRecv: 100 if rok { 101 L.Push(LTrue) 102 } else { 103 L.Push(LFalse) 104 } 105 L.Push(lv) 106 L.Call(2, 0) 107 case reflect.SelectSend: 108 L.Push(tbl.RawGetInt(3)) 109 L.Call(1, 0) 110 case reflect.SelectDefault: 111 L.Call(0, 0) 112 } 113 } 114 L.Push(LNumber(pos + 1)) 115 L.Push(lv) 116 if rok { 117 L.Push(LTrue) 118 } else { 119 L.Push(LFalse) 120 } 121 return 3 122 } 123 124 var channelMethods = map[string]LGFunction{ 125 "receive": channelReceive, 126 "send": channelSend, 127 "close": channelClose, 128 } 129 130 func channelReceive(L *LState) int { 131 rch := checkChannel(L, 1) 132 v, ok := rch.Recv() 133 if ok { 134 L.Push(LTrue) 135 L.Push(v.Interface().(LValue)) 136 } else { 137 L.Push(LFalse) 138 L.Push(LNil) 139 } 140 return 2 141 } 142 143 func channelSend(L *LState) int { 144 rch := checkChannel(L, 1) 145 v := checkGoroutineSafe(L, 2) 146 rch.Send(reflect.ValueOf(v)) 147 return 0 148 } 149 150 func channelClose(L *LState) int { 151 rch := checkChannel(L, 1) 152 rch.Close() 153 return 0 154 } 155 156 //