lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Am 06.10.2014 um 17:45 schröbte Thiago Padilha:
Hi

Hi!


- Why Lua has this limitation? I can't understand why a coroutine can't be
   created/yieled by call coming from C, and resumed after the C call returns.

Every Lua coroutine has its own Lua stack where local Lua variables are stored, so it is easy to yield (just stop using this stack) and resume later (start using that stack again). C uses a single global stack, so you can yield without problems (e.g. using longjmp), skipping stack frames, but you can't restore the stack frames once they are gone -- especially if that stack space has been reused. Coco solves this problem by giving each Lua coroutine a separate C stack as well, but this is not possible in portable ISO C.

- Is there a way I can reorganize my code work around this issue without relying
   on a patched lua VM or on luajit?(I want to publish the code to luarocks)

In Lua 5.2+ you have `lua_pcallk` and `lua_callk`, which will circumvent the problem by calling a separate function on resume. The C stack is still gone, so you have to save any data you need after the yield/resume on the Lua stack instead.

As a code example I have attached the `protect` factory from FinalizedExceptions[1], which supports yielding on Lua 5.2 and 5.3-alpha (and Lua 5.1 with Coco, probably).


Best regards

Thiago


Philipp

  [1]: http://lua-users.org/wiki/FinalizedExceptions


#include <stddef.h>
#include <lua.h>
#include <lauxlib.h>


#if LUA_VERSION_NUM < 502
#  define lua_pcallk( L, na, nr, err, ctx, cont ) \
  (lua_pcall( L, na, nr, err ))
#  define luaL_newlib( L, r ) \
  (lua_newtable( L ), luaL_register( L, NULL, r ))
#endif

#if LUA_VERSION_NUM < 503
typedef int lua_Ctx;
#endif

static int safecall_finish( lua_State* L, int status, lua_Ctx ctx ) {
  (void)ctx;
  if( status != 0 /* LUA_OK */ && status != LUA_YIELD ) {
    /* insert nil before error message and return nil, msg */
    lua_pushnil( L );
    lua_insert( L, 1 );
    return 2;
  } else /* return all values on the stack */
    return lua_gettop( L );
}

#if LUA_VERSION_NUM == 502
static int safecall_cont( lua_State* L ) {
  int ctx = 0;
  int status = lua_getctx( L, &ctx ); /* error or yield? */
  return safecall_finish( L, status, ctx );
}
#elif LUA_VERSION_NUM == 503
#define safecall_cont safecall_finish
#endif

static int safecall( lua_State* L ) {
  int status;
  int top = lua_gettop( L ); /* number of arguments */
  lua_pushvalue( L, lua_upvalueindex( 1 ) );
  lua_insert( L, 1 ); /* insert function from upvalue before args */
  status = lua_pcallk( L, top, LUA_MULTRET, 0, 0, safecall_cont );
  return safecall_finish( L, status, 0 );
}

static int protect( lua_State* L ) {
  lua_pushcclosure( L, safecall, 1 );
  return 1;
}


static luaL_Reg const funcs[] = {
  { "protect", protect },
  { NULL, NULL }
};

int luaopen_ltn13( lua_State* L ) {
  luaL_newlib( L, funcs );
  return 1;
}