Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lua co-routines

I'm trying to get an understanding of how I can use co-routines to "pause" a script and wait until some processing is done before resuming.

Perhaps I'm looking at co-routines in the wrong way. But my attempt is structured similar to the example given in this answer.

The loop in loop.lua never reaches a second iteration, and hence never reaches the i == 4 condition required to exit the running loop in the C code. If I do not yield in loop.lua, then this code performs as expected.

main.cpp

#include <lua/lua.hpp>

bool running = true;

int lua_finish(lua_State *) {
    running = false;
    printf("lua_finish called\n");
    return 0;
}
int lua_sleep(lua_State *L) {
    printf("lua_sleep called\n");
    return lua_yield(L,0);
}

int main() {
    lua_State* L = lua_open();
    luaL_openlibs(L);

    lua_register(L, "sleep", lua_sleep);
    lua_register(L, "finish", lua_finish);

    luaL_dofile(L, "scripts/init.lua");

    lua_State* cL = lua_newthread(L);
    luaL_dofile(cL, "scripts/loop.lua");

    while (running) {
        int status;
        status = lua_resume(cL,0);
        if (status == LUA_YIELD) {
            printf("loop yielding\n");
        } else {
            running=false; // you can't try to resume if it didn't yield
            // catch any errors below
            if (status == LUA_ERRRUN && lua_isstring(cL, -1)) {
                printf("isstring: %s\n", lua_tostring(cL, -1));
                lua_pop(cL, -1);
            }
        }
    }

    luaL_dofile(L, "scripts/end.lua");
    lua_close(L);
    return 0;
}

loop.lua

print("loop.lua")

local i = 0
while true do
    print("lua_loop iteration")
    sleep()

    i = i + 1
    if i == 4 then
        break
    end
end

finish()

EDIT: Added a bounty, to hopefully get some help on how to accomplish this.

like image 948
deceleratedcaviar Avatar asked Aug 26 '11 14:08

deceleratedcaviar


1 Answers

Return code 2 from lua_resume is a LUA_ERRRUN. Check the string on the top of the Lua stack to find the error message.

A similar pattern has worked for me, though I did use coroutine.yield instead of lua_yield and I'm in C rather than C++. I don't see why your solution wouldn't work

On your resume call, it's not clear if you're over simplifying for an example, but I'd make the following changes in your while loop:

int status;
status=lua_resume(cL,0);
if (status == LUA_YIELD) {
  printf("loop yielding\n");
}
else {
  running=false; // you can't try to resume if it didn't yield
  // catch any errors below
  if (status == LUA_ERRRUN && lua_isstring(cL, -1)) {
    printf("isstring: %s\n", lua_tostring(cL, -1));
    lua_pop(cL, -1);
  }
}

Edit 2:

For debugging, add the following before you run your resume. You've got a string getting pushed on the stack somewhere:

int status;
// add this debugging code
if (lua_isstring(cL, -1)) {
  printf("string on stack: %s\n", lua_tostring(cL, -1));
  exit(1);
}
status = lua_resume(cL,0);

Edit 3:

Oh good grief, I can't believe I didn't see this already, you don't want to run luaL_dofile when you're going to yield because you can't yield a pcall directly as best I know, which is what happens in the dofile (5.2 will pass it through, but I think you still need the lua_resume). Switch to this:

luaL_loadfile(cL, "scripts/loop.lua");
like image 104
BMitch Avatar answered Sep 30 '22 14:09

BMitch