2
votes

I have a network application that uses Lua scripts. Upon starting the application I create a global Lua state and load all script files, that contain various functions, and for every client that connects I create a Lua thread, for that connection.

// On start
var GL = luaL_newstate();
// register functions...
// load scripts...

// On connection
connection.State = lua_newthread(GL);

When a request that uses a script comes in, I get the global function and call it.

var NL = connection.State;

var result = lua_resume(NL, 0);
if (result != 0 && result != LUA_YIELD)
{
    // error...
    result = 0;
}

if (result == 0)
{
    // function returned...
}

Now, some scripts require a response to something from the client, so I yield in those functions, to wait for it. When the response comes in, the script is resumed with lua_resume(NL, 1).

// Lua
text("How are you?")
local response = select("Good", "Bad")

// Host
private int select(IntPtr L)
{
    // send response request...
    return lua_yield(L, 1);
}

// On response
lua_pushstring(NL, response);
var result = lua_resume(NL, 1);
// ...

My problem is that I need to be able to cancel that yield, and return from the Lua function, without executing any more code in the Lua function, and without adding additional code to the scripts. In other words, I basically want to make the Lua thread throw an exception, get back to the start, and forget it ever executed that function.

Is that possible?

One thing I thought might work, but didn't, was calling lua_error. The result was an SEHException on the lua_error call. I assume because the script isn't currently running, but yielding.

1

1 Answers

0
votes

While I didn't find a way to wipe a thread's slate clean (I don't think it's possible), I did find a solution in figuring out how lua_newthread works.

When the thread is created, the reference to it is put on the "global" state's stack, and it doesn't get collected until it's removed from there. All you have to do to clean up the thread is removing it from the stack with lua_remove. This requires you to create new threads regularly, but that's not much of a problem for me.

I'm now keeping track of the created threads and their index on the stack, so I can removed them when I'm done with them for whatever reason (cancel, error, etc). All other indices are updated, as the removal will shift the ones that came after it.

if (sessionOver)
{
    lua_remove(GL, thread.StackIndex);

    foreach (var t in threads)
    {
        if (t.StackIndex > thread.StackIndex)
            t.StackIndex--;
    }
}