Giter VIP home page Giter VIP logo

Comments (5)

yuin avatar yuin commented on July 30, 2024

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.

yunyet avatar yunyet commented on July 30, 2024

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.

xrash avatar xrash commented on July 30, 2024

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.

yuin avatar yuin commented on July 30, 2024

@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.

xrash avatar xrash commented on July 30, 2024

@yuin ok :(

Really thank you for helping out!

from gopher-lua.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.