mirror of https://github.com/tidwall/tile38.git
185 lines
3.7 KiB
Go
185 lines
3.7 KiB
Go
package lua
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
func checkChannel(L *LState, idx int) reflect.Value {
|
|
ch := L.CheckChannel(idx)
|
|
return reflect.ValueOf(ch)
|
|
}
|
|
|
|
func checkGoroutineSafe(L *LState, idx int) LValue {
|
|
v := L.CheckAny(2)
|
|
if !isGoroutineSafe(v) {
|
|
L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable")
|
|
}
|
|
return v
|
|
}
|
|
|
|
func OpenChannel(L *LState) int {
|
|
var mod LValue
|
|
//_, ok := L.G.builtinMts[int(LTChannel)]
|
|
// if !ok {
|
|
mod = L.RegisterModule(ChannelLibName, channelFuncs)
|
|
mt := L.SetFuncs(L.NewTable(), channelMethods)
|
|
mt.RawSetString("__index", mt)
|
|
L.G.builtinMts[int(LTChannel)] = mt
|
|
// }
|
|
L.Push(mod)
|
|
return 1
|
|
}
|
|
|
|
var channelFuncs = map[string]LGFunction{
|
|
"make": channelMake,
|
|
"select": channelSelect,
|
|
}
|
|
|
|
func channelMake(L *LState) int {
|
|
buffer := L.OptInt(1, 0)
|
|
L.Push(LChannel(make(chan LValue, buffer)))
|
|
return 1
|
|
}
|
|
|
|
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{
|
|
Dir: reflect.SelectSend,
|
|
Chan: reflect.ValueOf(nil),
|
|
Send: 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
|
|
}
|
|
|
|
if L.ctx != nil {
|
|
cases = append(cases, reflect.SelectCase{
|
|
Dir: reflect.SelectRecv,
|
|
Chan: reflect.ValueOf(L.ctx.Done()),
|
|
Send: reflect.ValueOf(nil),
|
|
})
|
|
}
|
|
|
|
pos, recv, rok := reflect.Select(cases)
|
|
|
|
if L.ctx != nil && pos == L.GetTop() {
|
|
return 0
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
var channelMethods = map[string]LGFunction{
|
|
"receive": channelReceive,
|
|
"send": channelSend,
|
|
"close": channelClose,
|
|
}
|
|
|
|
func channelReceive(L *LState) int {
|
|
rch := checkChannel(L, 1)
|
|
var v reflect.Value
|
|
var ok bool
|
|
if L.ctx != nil {
|
|
cases := []reflect.SelectCase{{
|
|
Dir: reflect.SelectRecv,
|
|
Chan: reflect.ValueOf(L.ctx.Done()),
|
|
Send: reflect.ValueOf(nil),
|
|
}, {
|
|
Dir: reflect.SelectRecv,
|
|
Chan: rch,
|
|
Send: reflect.ValueOf(nil),
|
|
}}
|
|
_, v, ok = reflect.Select(cases)
|
|
} else {
|
|
v, ok = rch.Recv()
|
|
}
|
|
if ok {
|
|
L.Push(LTrue)
|
|
L.Push(v.Interface().(LValue))
|
|
} else {
|
|
L.Push(LFalse)
|
|
L.Push(LNil)
|
|
}
|
|
return 2
|
|
}
|
|
|
|
func channelSend(L *LState) int {
|
|
rch := checkChannel(L, 1)
|
|
v := checkGoroutineSafe(L, 2)
|
|
rch.Send(reflect.ValueOf(v))
|
|
return 0
|
|
}
|
|
|
|
func channelClose(L *LState) int {
|
|
rch := checkChannel(L, 1)
|
|
rch.Close()
|
|
return 0
|
|
}
|
|
|
|
//
|