[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Why I can't resume a coroutine created in a previous call to a C function?
- From: Philipp Janda <siffiejoe@...>
- Date: Mon, 06 Oct 2014 18:53:00 +0200
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;
}