8

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.

Community
  • 1
  • 1
hiddensunset4
  • 5,825
  • 3
  • 39
  • 61

2 Answers2

4

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");
BMitch
  • 231,797
  • 42
  • 475
  • 450
  • The error code 2 only occurs when the resume code is successively run (more than one, without another yield). Any other error I get is: "attempt to call a string value". The pop code: `if (lua_isstring(cL, -1)) { printf("isstring: %s\n", lua_tostring(cL, -1)); lua_pop(cL, -1); }` – hiddensunset4 Aug 27 '11 at 13:35
  • "attempt to call a string" means something else is on the stack other than your routine, possibly an error message. And when resume finishes with a return 0, the routine is done and you can't resume it any more, it's no longer on the stack. You can only resume again if you got a yield last time or pushed a new routine on the stack. – BMitch Aug 27 '11 at 17:23
  • How can I get the error message, if not through the code above? – hiddensunset4 Aug 28 '11 at 15:51
  • I don't think we have enough code to give you a good answer. You say that you're checking for an error message on the stack, but then you're trying to call a string value when you run the resume? I'll update my answer with at least one tip. – BMitch Aug 28 '11 at 17:18
  • Thanks for the update, however thats the exact same control sequence I was using :/. Ill give it another shot tonight. – hiddensunset4 Aug 28 '11 at 22:22
  • @Daniel: any luck? If not, some more code on your while loop and error handling may help. – BMitch Aug 30 '11 at 16:31
  • none yet, but I havent tried your full permutation yet. Uni's kept me busy writing assembly (lol). – hiddensunset4 Aug 30 '11 at 19:18
  • `init.lua \n loop.lua \n lua_loop iteration \n lua_sleep called \n isstring: attempt to call a string value \n end.lua \n` It appears it is resuming the thread, hence only the non existent error message. But if I let it loop (ie remove `running = false`) then it attempts to resume the thread, which hasn't actually run, and errors: `isstring: cannot resume non-suspended coroutine `. Updated original code. – hiddensunset4 Sep 01 '11 at 02:21
  • @Daniel: once it errors, the thread is dead, and you have to create a new one. I don't see anything that's pushing a string on the stack. It's worth popping that string off and printing it after the lua_resume yields to see what's there. – BMitch Sep 01 '11 at 02:38
  • @Daniel: note that in 5.2, you can yield through a pcall which would let you catch an error and reuse a thread (though not resume it since the error would pop the function off the stack). – BMitch Sep 01 '11 at 02:40
  • @Daniel: see my 3rd edit, you need the loadfile instead of dofile for code that can yield. – BMitch Sep 01 '11 at 02:48
  • 1
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH. luaL_loadfile(cL, "scripts/loop.lua"); It all works now... *crys*. Thank you! – hiddensunset4 Sep 01 '11 at 02:51
  • 1
    @Daniel, you're welcome, still can't believe I didn't see that until now. – BMitch Sep 01 '11 at 02:54
1

Last time i was messing with Lua coroutines I ended with such code

const char *program =
"function hello()\n"
"  io.write(\"Hello world 1!\")\n"
"  io.write(\"Hello world 2!\")\n"
"  io.write(\"Hello world 3!\")\n"
"end\n"
"function hate()\n"
"  io.write(\"Hate world 1!\")\n"
"  io.write(\"Hate world 2!\")\n"
"  io.write(\"Hate world 3!\")\n"
"end\n";

const char raw_program[] = 
"function hello()\n"
"  io.write(\"Hello World!\")\n"
"end\n"
"\n"
"cos = {}\n"
"\n"
"for i = 0, 1000, 1 do\n"
"  cos[i] = coroutine.create(hello)\n"
"end\n"
"\n"
"for i = 0, 1000, 1 do\n"
"  coroutine.resume(cos[i])\n"
"end";

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = lua_open();
    lua_State *Lt[1000];
    global_State *g = G(L);

    printf("Lua memory usage after open: %d\n", g->totalbytes);

    luaL_openlibs(L);

    printf("Lua memory usage after openlibs: %d\n", g->totalbytes);

    lua_checkstack(L, 2048);

    printf("Lua memory usage after checkstack: %d\n", g->totalbytes);

    //lua_load(L, my_lua_Reader, (void *)program, "code");
    luaL_loadbuffer(L, program, strlen(program), "line");

    printf("Lua memory usage after loadbuffer: %d\n", g->totalbytes);

    int error = lua_pcall(L, 0, 0, 0);
    if (error) {
        fprintf(stderr, "%s", lua_tostring(L, -1));
        lua_pop(L, 1);
    }

    printf("Lua memory usage after pcall: %d\n", g->totalbytes);

    for (int i = 0; i < 1000; i++) {
        Lt[i] = lua_newthread(L);
        lua_getglobal(Lt[i], i % 2 ? "hello" : "hate");
    }

    printf("Lua memory usage after creating 1000 threads: %d\n", g->totalbytes);

    for (int i = 0; i < 1000; i++) {
        lua_resume(Lt[i], 0);
    }

    printf("Lua memory usage after running 1000 threads: %d\n", g->totalbytes);

    lua_close(L);

    return 0;
}

It seems you could not load file as coroutine? but use function instead And it should be selected to the top of the stack.

lua_getglobal(Lt[i], i % 2 ? "hello" : "hate");
yatagarasu
  • 550
  • 6
  • 13
  • 1
    No need to create the coroutine in the Lua script, the `lua_newthread` call does that in C/C++. I use it myself without any issues. – BMitch Aug 30 '11 at 16:29
  • Actually I don't load raw_program. I should cut it from my post. But yes I see. The problem is with resuming coroutine. not starting them. should read original post carefully – yatagarasu Aug 30 '11 at 21:08
  • Just saw that you're using `lua_newthread` in your example. But yeah, OP has issues after yielding the coroutine. – BMitch Aug 30 '11 at 21:22