Alex Roitman b55300b729 Lua scripting feature. (#224)
* Start on lua scripting

* Implement evalsha, script load, script exists, and script flush

* Type conversions from lua to resp/json.
Refactor to make luastate and luascripts persistent in the controller.

* Change controller.command and all underlying commands to return resp.Value.
Serialize only during the ouput.

* First stab at tile38 call from lua

* Change tile38 into tile38.call in Lua

* Property return errors from scripts

* Minor refactoring.  No locking on script run

* Cleanup/refactoring

* Create a pool of 5 lua states, allow for more as needed. Refactor.

* Use safe map for scripts.  Add a limit for max number of lua states.  Refactor.

* Refactor

* Refactor script commands into atomic, read-only, and non-atomic classes.
Proper locking for all three classes.
Add tests for scripts

* More tests for scripts

* Properly escape newlines in lua-produced errors

* Better test for readonly failure

* Correctly convert ok/err messages between lua and resp.
Add pcall, sha1hex, error_reply, status_reply functions to tile38 namespace in lua.

* Add pcall test. Change writeErr to work with string argument

* Make sure eval/evalsha never attempt to write AOF

* Add eval-set and eval-get to benchmarks

* Fix eval benchmark tests, add more

* Improve benchmarks

* Optimizations and refactoring.

* Add lua memtest

* Typo

* Add dependency

* golint fixes

* gofmt fixes

* Add scripting commands to the core/commands.json

* Use ARGV for args inside lua
2017-10-05 08:20:40 -07:00

153 lines
3.1 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{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
}
var channelMethods = map[string]LGFunction{
"receive": channelReceive,
"send": channelSend,
"close": channelClose,
}
func channelReceive(L *LState) int {
rch := checkChannel(L, 1)
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
}
//