Search code examples
luayieldmscapi

how to yield lua script in C Function


it works when lua call a C API if a C function call lua function, and the lua function call C API, longjmp error

lua_yieldk, lua_callk, and lua_pcallk how does it work?

my c code:

int trace(lua_State *L)
{
    const char *str = luaL_checkstring(L, 1);
    printf("%d:%s\n", GetTickCount(), str);
    return 1;
 }

int pause(lua_State *L)
{
    printf("pause");
    return lua_yield(L, 0);
}

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = luaL_newstate(); 
    luaL_openlibs(L);
    lua_pushcfunction( L, pause );
    lua_setglobal( L, "pause" );
    lua_pushcfunction( L, trace );
    lua_setglobal( L, "trace" );
    if (luaL_loadfile(L, "test.lua"))
       error(L, "cannot run script %s\n", lua_tostring(L,-1));
    lua_resume(L, NULL, 0);
        lua_getglobal(L, "t");
    lua_pcallk(L, 0, 0, 0, 0, 0);
    lua_resume(L, NULL, 0);
    lua_resume(L, NULL, 0);
    lua_resume(L, NULL, 0);
    lua_resume(L, NULL, 0);
    lua_close(L);
    getchar();
    return 0;
}

lua code

function t()
pause(2)
pause(2)
pause(2)
pause(2)
end

Solution

  • You call lua_resume on a thread returned by lua_newthread, not lua_newstate.

    So in your code you would either have to change the first lua_resume to lua_(p)call:

    if (luaL_loadfile(L, "test.lua"))
       error(L, "cannot run script %s\n", lua_tostring(L,-1));
    lua_pcall(L, 0, 0, 0);
    

    or swap luaL_loadfile for luaL_dofile:

    if (luaL_dofile(L, "test.lua"))
       error(L, "cannot run script %s\n", lua_tostring(L,-1));
    //lua_resume(L, NULL, 0); Not necessary anymore
    

    I'm not relating to the efficiency of setting the global t here.

    Now to the main point of the question:

    • First, each call to lua_callk, lua_pcallk or lua_yieldk needs to receive a continuation function as an argument. In your case it's 0. Actually, lua_yieldk can take 0 as continuation function, but then control is passed back to Lua script, where the call to the C function occured.
    • Next, any call to those functions must be made within a coroutine thread, not the main thread.
    • And lastly, you cannot yield across C call boundary. That is, when you call lua_pcallk and the chunk that pcallk is calling yields, the continuation function is executed. However, you cannot have lua_pcallk call a Lua function that in turn calls a C function that yields (pause in your example). That's forbidden.

    An example of lua_pcallk:

    int cont(lua_State *L)
    {
        getchar();
        return 0;
    }
    
    int pcallktest(lua_State *L)
    {
        luaL_loadstring(L, "yield()");
        int test = lua_pcallk(L, 0, 0, 0, 0, cont);
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        lua_State *T = lua_newthread(L);
    
        luaL_loadfile(T, "Test.lua");
        lua_pushcfunction(T, pcallktest);
        lua_resume(T, NULL, 1);
        return 0;
    }
    

    Lua code:

    local pcallktest = ...
    pcallktest()
    

    Now this piece of code starts a new coroutine from file "Test.lua". The Lua code calls C function pcallktest, which in turn calls lua_pcallk on another Lua function, which just yields. When the yield occurs, execution jumps (longjmp) to the cont function, which was provided as an argument to lua_pcallk. When the cont function returns, the coroutine execution ends and lua_resume from the _tmain returns.

    An example of lua_yieldk:

    int cont(lua_State *L)
    {
        getchar();
        return 0;
    }
    
    int yieldktest(lua_State *L)
    {
        return lua_yieldk(L, 0, 0, cont);
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        lua_State *T = lua_newthread(L);
    
        luaL_loadfile(T, "Test.lua");
        lua_pushcfunction(T, yieldktest);
        lua_resume(T, NULL, 1);
        lua_resume(T, NULL, 0);
        return 0;
    }
    

    Lua code:

    local yieldktest = ...
    yieldktest()
    

    This bit in turn executes coroutine that yields from within a C function (yieldktest). When the coroutine is then resumed (the second lua_resume), control is passed back to the continuation function cont, which executes as a continuation of yieldktest.

    These examples do not deal with lua_getctx and stack states, but merely demonstrate the mechanisms of those functions.