Comments (5)
Like in the original Lua implementation, GopherLua has no official way to clone an LState[1].
It is recommended to create individual LStates and share values through channels
1 : Cloning Lua state, Clone a lua state
from gopher-lua.
So,*lua.LState.DoString()
is not really thread safe or go-routine safe?
Does it necessary to use a pool to reuse the *lua.LState
?
type LuaStatesPool struct {
pool chan *lua.LState
}
func NewLuaPool(n int) *LuaStatesPool {
if n < 0 {
n = 64
}
lp := &LuaStatesPool{}
lp.pool = make(chan *lua.LState, n)
return lp
}
func (this *LuaStatesPool) Get() *lua.LState {
var state *lua.LState
select {
case state = <-this.pool:
// pass
default:
state = lua.NewState()
}
// preload modules
state.PreloadModule("INJ", module.Loader)
return state
}
func (this *LuaStatesPool) Put(state *lua.LState) {
select {
case this.pool <- state:
return
default:
state.Close()
}
}
Use:
go func() {
t := time.Now()
L := LPool.Get()
defer LPool.Put(L)
log.Printf(" VM prepared ----%v ", time.Since(t))
if err := L.DoString(script); err != nil {
panic(err)
}
}()
Please give me some better practice.
from gopher-lua.
Hi, thanks for your time to answer this!
Using a pool of LStates is not actually a solution to my problem, but I really appreciate your effort and code! It is really cool!
I will try to explain thoroughly what I am doing, so there is no confusion.
I want to define functions in Lua, and execute them in Go, in arbitrary times, concurrently. Example:
emit(function()
print("function one")
end)
emit(function()
print("function two")
end)
Later, I run them like this:
if err := L.CallByParam(lua.P{
Fn: emittedFn,
NRet: 0,
Protect: true,
}); err != nil {
panic(err)
}
The problem is: I cannot run these emitted functions concurrently in the same LState, because one LState has one stack and thus there would be unexpected behavior (crash). Also, because I must be able to kill each function individually (which can be achieved by generating an error in the LState).
The solution is to have multiple LStates, one for each function. Given that the functions must be defined only once, in one LState, constructing one "parent" LState and then cloning it seems like the natural way. It is not possible to clone LStates, however.
So, using a pool is a cool way of maintaining several LStates, but it is unrelated to the problem. The problem is about defining functions once and running them later, concurrently.
I came to realize this code actually works:
package main
import (
"github.com/yuin/gopher-lua"
)
var script = `
emit(function()
print("hello, lua!")
end)
`
func main() {
L := lua.NewState()
defer L.Close()
// Create a variable to hold the emitted function.
var emittedFn *lua.LFunction
// Set the "emit" function in the Lua state.
L.SetGlobal("emit", L.NewFunction(func(L *lua.LState) int {
emittedFn = L.ToFunction(1)
return 0
}))
// Run the script which will emit the function.
if err := L.DoString(script); err != nil {
panic(err)
}
// Create another Lua state.
L2 := lua.NewState()
defer L2.Close()
// Call the function emitted from the *first*
// Lua state in the *second* Lua state.
if err := L2.CallByParam(lua.P{
Fn: emittedFn,
NRet: 0,
Protect: true,
}); err != nil {
panic(err)
}
}
This is getting an LFunction from one LState and executing it in another LState. This way, each LState is completely independent. The problem is I don't know how much I can trust that code, since the LFunction has references to code from its original LState. Nonetheless, It seems to work.
So... Do you guys have any comment about the above code? Is it safe to run an LFunction from one LState in another? Safe meaning it wont crash. I am not worried about shared memory leading to unexpected values, but I am worried about possible crashes.
Thanks!
from gopher-lua.
@yunyet , @xrash , Like in the original Lua implementation, *lua.LState
and *lua.LFunction
are NOT goroutine-safe. So it is wrong of @xrash to use an LFunction defined in other LStates even though it works at present.
As described at the my first comment, It is recommended to create individual LStates and share values through channels.
Example:
package main
import (
"github.com/yuin/gopher-lua"
"time"
)
var luaGlobal chan lua.LValue
var script = `
emit(function()
local ok, v = sharedvalue:receive()
print("hello, lua! shared value = " .. tostring(v))
sharedvalue:send(v + 1)
end)
`
func newLuaVm() *lua.LState {
L := lua.NewState()
L.SetGlobal("emit", L.NewFunction(func(L *lua.LState) int {
L.SetGlobal("_emmitedFn", L.ToFunction(1))
return 0
}))
L.SetGlobal("sharedvalue", lua.LChannel(luaGlobal))
if err := L.DoString(script); err != nil {
panic(err)
}
return L
}
func main() {
luaGlobal = make(chan lua.LValue, 1)
luaGlobal <- lua.LNumber(1)
L1 := newLuaVm()
L2 := newLuaVm()
fn := func(L *lua.LState) {
for i := 0; i < 10; i++ {
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("_emmitedFn"),
NRet: 0,
Protect: true,
}); err != nil {
panic(err)
}
time.Sleep(1 * time.Second)
}
}
go fn(L1)
go fn(L2)
time.Sleep(11 * time.Second)
}
from gopher-lua.
@yuin ok :(
Really thank you for helping out!
from gopher-lua.
Related Issues (20)
- io.open(filename, "w") should truncate file
- 【Help】Panic when call Close(), May be bug? HOT 1
- [ HELP!] How to revert Lua AST to lua source code?
- Preload other lua files
- L.DoFile does not work when i require a so in lua script
- Wrong result of multi-assignment HOT 2
- Function names do not support UTF-8/Unicode
- Virtual machine interfaces need to be advertised
- bug: pcall affecting function upvalues HOT 4
- xpcall with error in error handler returns (nil, nil)
- The parameter passed by xpcall is nil
- different behavior in read function HOT 1
- Expand fopen modes
- different behavior in io.popen function
- Incorrect behavior for string.gmatch
- stuck when xpcall HOT 3
- LNumber.String() use strconv instead of fmt.Sprint HOT 1
- Can't require lua-cjson
- tonumber doesn't handle E notation correctly HOT 1
- 【BUG】panic: runtime error: index out of range [1048577] with length 1048577 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gopher-lua.