func runTest1(state *lua.LState) {
defer func() {
logger.Print(recover())
}()
buff, err := Asset("test1.lua")
if err != nil {
log.Fatal(err)
}
biosThread := state.NewThread()
biosThread.Panic = func(state *lua.LState) {
log.Fatal(state.Get(1))
}
biosFunc, err := state.Load(bytes.NewReader(buff), "test1.lua")
if err != nil {
log.Fatal(err)
}
tickTimer := time.NewTicker(time.Second / 30)
defer tickTimer.Stop()
logger.Printf("Starting main loop.")
logger.Printf("Status of biosThread: %s", state.Status(biosThread))
biosState, err, args := state.Resume(biosThread, biosFunc)
logger.Printf("Got state: %s", biosState)
for {
if biosState == lua.ResumeError {
logger.Print("Main thread error: ", err)
return
} else if biosState == lua.ResumeYield || biosState == lua.ResumeOK {
event := <-events
logger.Print("Sending event %q into lua-land", event.Name)
evtFilter := lua.LVAsString(args[0])
if evtFilter != "" && event.Name == evtFilter {
eventArgs := []lua.LValue{
lua.LString(event.Name),
}
eventArgs = append(eventArgs, event.Args...)
biosState, err, args = state.Resume(biosThread, biosFunc, eventArgs...)
} else {
<-tickTimer.C
}
}
}
}
@yuin Hi Yusuke!
Me and our team just comprehend some code in last few days and have a little question about coroutines and channels in Gopher Lua.
https://github.com/yuin/gopher-lua/blob/master/coroutinelib.go
https://github.com/yuin/gopher-lua/blob/master/_glua-tests/coroutine.lua
https://github.com/yuin/gopher-lua/blob/master/channellib.go
https://github.com/yuin/gopher-lua/blob/master/channellib_test.go
It's really good if we can have a coroutines and channels for CSP (as in Go) in Lua and mapping'em to goroutines in the Go run-time - this would be very efficient upon use of resources.
So, I understand that I just need to declare some definitions to include Lua modules, in Gopher Lua, like it mentioned in source (https://github.com/yuin/gopher-lua/blob/master/linit.go#L31) and mentioned in example (https://github.com/yuin/gopher-lua#opening-a-subset-of-builtin-modules):
var luaLibs = []luaLib{
luaLib{LoadLibName, OpenPackage},
luaLib{BaseLibName, OpenBase},
luaLib{TabLibName, OpenTable},
luaLib{IoLibName, OpenIo},
luaLib{OsLibName, OpenOs},
luaLib{StringLibName, OpenString},
luaLib{MathLibName, OpenMath},
luaLib{DebugLibName, OpenDebug},
luaLib{ChannelLibName, OpenChannel},
luaLib{CoroutineLibName, OpenCoroutine},
}
Is it really would work and we can write some beautiful Lua code with coroutines and channels... or... The only tricky part I'm unsure of with coroutines is how the coroutine maintains a copy of the LState when it executes.
I'm unclear on the internal VM implementation around how the LState is shared across goroutines and whether it will incur race conditions. Here's the question.
So we can face with shared mutable state, dead-lock of threads or/and race conditions. I assume we can also use Go race detection tools (https://blog.golang.org/race-detector, https://golang.org/doc/articles/race_detector.html).
If we build the binaries (of Gopher Lua) with --race
the race detector will dump stack traces if we face with race conditions in the execution of Lua code with coroutines.
https://github.com/yuin/gopher-lua/blob/master/README.rst#goroutines
The LState is not goroutine-safe. It is recommended to use one LState per goroutine and communicate between goroutines by using channels.
Channels are represented by channel objects in GopherLua. And a channel table provides functions for performing channel operations.
Some objects can not be sent over channels due to having non-goroutine-safe objects inside itself.
a thread(state)
a function
an userdata
a table with a metatable
You must not send these objects from Go APIs to channels.
Docs about LState type contains more details:
https://godoc.org/github.com/yuin/gopher-lua#LState
https://github.com/yuin/gopher-lua/blob/master/auxlib.go#L92
https://github.com/yuin/gopher-lua/blob/master/auxlib.go#L434
https://github.com/yuin/gopher-lua/blob/master/state.go#L68
https://github.com/yuin/gopher-lua/blob/master/state.go#L1207
https://github.com/yuin/gopher-lua/blob/master/state.go#L1726
https://github.com/yuin/gopher-lua/blob/master/state.go#L1738
https://github.com/yuin/gopher-lua/blob/master/state.go#L1798
I think the potential risk in Gopher Lua interpreter is low, but still be present, similarly as in plane Go with goroutines usage (as Bras Fitzpatrick said in his presentation "Go 90% perfect 100% of time").
L.Get(1)
(L is the lua state) to get the table
Tmpl = {
firstName = {"bar"},
lastName = {"bar"},
phoneNumber = {"bar"},
email = {"bar"},
status = {"bar"},
modifiers = {
test = "hi",
firstName = Capitalize,
lastName = Capitalize
},
}
function Capitalize(str)
return str:sub(1,1):upper()..str:sub(2):lower()
end
panic: attempt to call a non-function object
stack traceback:
[G]: ?
func state(l *lua.LState, s int) func() {
return func() {
l.ToFunction(s)
}
}