From 47eb3f94373a13ac9f204ca65dfde602f53bdc1a Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 17 Feb 2014 11:05:19 +0100 Subject: Deep userdata support improvements * bumped version to 3.9.0 * keepers now require "package", receive package.path & package.cpath, and call on_state_create() if it is a C function * changed the deep public API (improved deep idfunc signature, renamed luaG_deep_userdata to luaG_newdeepuserdata) * if an error occurs while copying a deep userdata, don't raise inside the keeper state * fixed situations where raised errors could lead to memory leaks (deep gc) --- src/deep.h | 12 +- src/keeper.c | 44 +++- src/lanes.c | 286 ++++++++++----------- src/tools.c | 791 ++++++++++++++++++++++++++++------------------------------- src/tools.h | 16 +- 5 files changed, 575 insertions(+), 574 deletions(-) (limited to 'src') diff --git a/src/deep.h b/src/deep.h index 1315157..e1f2c4f 100644 --- a/src/deep.h +++ b/src/deep.h @@ -15,9 +15,17 @@ #define LANES_API #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) -typedef void (*luaG_IdFunction)( lua_State* L, char const* const which); +enum eDeepOp +{ + eDO_new, + eDO_delete, + eDO_metatable, + eDO_module, +}; -extern LANES_API int luaG_deep_userdata( lua_State* L, luaG_IdFunction idfunc); +typedef void* (*luaG_IdFunction)( lua_State* L, enum eDeepOp op_); + +extern LANES_API int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc); extern LANES_API void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index); #endif // __LANES_DEEP_H__ diff --git a/src/keeper.c b/src/keeper.c index c22bfed..4eb8bb3 100644 --- a/src/keeper.c +++ b/src/keeper.c @@ -44,6 +44,7 @@ #include #include "lua.h" +#include "lualib.h" #include "lauxlib.h" #include "threading.h" @@ -623,11 +624,10 @@ char const* init_keepers( lua_State* L) int i; PROPAGATE_ALLOCF_PREP( L); - STACK_CHECK( L); - lua_getfield( L, 1, "nb_keepers"); + STACK_CHECK( L); // L K + lua_getfield( L, 1, "nb_keepers"); // nb_keepers GNbKeepers = (int) lua_tointeger( L, -1); - lua_pop( L, 1); - STACK_END( L, 0); + lua_pop( L, 1); // assert( GNbKeepers >= 1); GKeepers = malloc( GNbKeepers * sizeof( struct s_Keeper)); @@ -640,16 +640,37 @@ char const* init_keepers( lua_State* L) } STACK_CHECK( K); + // make sure 'package' is initialized in keeper states, so that we have require() + // this because this is needed when transfering deep userdata object + luaL_requiref( K, "package", luaopen_package, 1); // package + lua_pop( K, 1); // + STACK_MID( K, 0); + serialize_require( K); + STACK_MID( K, 0); + + // copy package.path and package.cpath from the source state + lua_getglobal( L, "package"); // package + if( !lua_isnil( L, -1)) + { + luaG_inter_copy_package( L, K, -1, eLM_ToKeeper); + } + lua_pop( L, 1); // + STACK_MID( L, 0); + + // attempt to call on_state_create(), if we have one and it is a C function + // (only support a C function because we can't transfer executable Lua code in keepers) + call_on_state_create( K, L, eLM_ToKeeper); + // to see VM name in Decoda debugger - lua_pushliteral( K, "Keeper #"); - lua_pushinteger( K, i + 1); - lua_concat( K, 2); - lua_setglobal( K, "decoda_name"); + lua_pushliteral( K, "Keeper #"); // "Keeper #" + lua_pushinteger( K, i + 1); // "Keeper #" n + lua_concat( K, 2); // "Keeper #n" + lua_setglobal( K, "decoda_name"); // // create the fifos table in the keeper state - lua_pushlightuserdata( K, fifos_key); - lua_newtable( K); - lua_rawset( K, LUA_REGISTRYINDEX); + lua_pushlightuserdata( K, fifos_key); // fifo_key + lua_newtable( K); // fifo_key {} + lua_rawset( K, LUA_REGISTRYINDEX); // STACK_END( K, 0); // we can trigger a GC from inside keeper_call(), where a keeper is acquired @@ -660,6 +681,7 @@ char const* init_keepers( lua_State* L) #if HAVE_KEEPER_ATEXIT_DESINIT atexit( atexit_close_keepers); #endif // HAVE_KEEPER_ATEXIT_DESINIT + STACK_END( L, 0); return NULL; // ok } diff --git a/src/lanes.c b/src/lanes.c index dbb0a82..597ac4b 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -52,7 +52,7 @@ * ... */ -char const* VERSION = "3.8.5"; +char const* VERSION = "3.9.0"; /* =============================================================================== @@ -422,11 +422,11 @@ struct s_Linda char name[1]; }; -static void linda_id( lua_State*, char const * const which); +static void* linda_id( lua_State*, enum eDeepOp); static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) { - struct s_Linda* linda = luaG_todeep( L, linda_id, idx_); + struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); return linda; } @@ -996,7 +996,7 @@ LUAG_FUNC( linda_deep) static int linda_tostring( lua_State* L, int idx_, bool_t opt_) { - struct s_Linda* linda = luaG_todeep( L, linda_id, idx_); + struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); if( !opt_) { luaL_argcheck( L, linda, idx_, "expecting a linda object"); @@ -1084,122 +1084,127 @@ LUAG_FUNC( linda_dump) * For any other strings, the ID function must not react at all. This allows * future extensions of the system. */ -static void linda_id( lua_State* L, char const* const which) +static void* linda_id( lua_State* L, enum eDeepOp op_) { - if( strcmp( which, "new" ) == 0) + switch( op_) { - struct s_Linda* s; - size_t name_len = 0; - char const* linda_name = NULL; - int const top = lua_gettop( L); - - if( top > 0 && lua_type( L, top) == LUA_TSTRING) + case eDO_new: { - linda_name = lua_tostring( L, top); - name_len = strlen( linda_name); - } + struct s_Linda* s; + size_t name_len = 0; + char const* linda_name = NULL; + int const top = lua_gettop( L); - /* The deep data is allocated separately of Lua stack; we might no - * longer be around when last reference to it is being released. - * One can use any memory allocation scheme. - */ - s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included - ASSERT_L( s); - - SIGNAL_INIT( &s->read_happened); - SIGNAL_INIT( &s->write_happened); - s->simulate_cancel = CANCEL_NONE; - s->name[0] = 0; - memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); + if( top > 0 && lua_type( L, top) == LUA_TSTRING) + { + linda_name = lua_tolstring( L, top, &name_len); + } - lua_pushlightuserdata( L, s); - } - else if( strcmp( which, "delete") == 0) - { - struct s_Keeper* K; - struct s_Linda* l = lua_touserdata( L, 1); - ASSERT_L( l); + /* The deep data is allocated separately of Lua stack; we might no + * longer be around when last reference to it is being released. + * One can use any memory allocation scheme. + */ + s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included + ASSERT_L( s); + + SIGNAL_INIT( &s->read_happened); + SIGNAL_INIT( &s->write_happened); + s->simulate_cancel = CANCEL_NONE; + s->name[0] = 0; + memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); + return s; + } - /* Clean associated structures in the keeper state. - */ - K = keeper_acquire( l); - if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) + case eDO_delete: { - keeper_call( K->L, KEEPER_API( clear), L, l, 0); + struct s_Keeper* K; + struct s_Linda* l = lua_touserdata( L, 1); + ASSERT_L( l); + + /* Clean associated structures in the keeper state. + */ + K = keeper_acquire( l); + if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) + { + keeper_call( K->L, KEEPER_API( clear), L, l, 0); + } + keeper_release( K); + + /* There aren't any lanes waiting on these lindas, since all proxies + * have been gc'ed. Right? + */ + SIGNAL_FREE( &l->read_happened); + SIGNAL_FREE( &l->write_happened); + free( l); + return NULL; } - keeper_release( K); - /* There aren't any lanes waiting on these lindas, since all proxies - * have been gc'ed. Right? - */ - SIGNAL_FREE( &l->read_happened); - SIGNAL_FREE( &l->write_happened); - free( l); - } - else if( strcmp( which, "metatable" ) == 0) - { + case eDO_metatable: + { - STACK_CHECK( L); - lua_newtable( L); - // metatable is its own index - lua_pushvalue( L, -1); - lua_setfield( L, -2, "__index"); + STACK_CHECK( L); + lua_newtable( L); + // metatable is its own index + lua_pushvalue( L, -1); + lua_setfield( L, -2, "__index"); - // protect metatable from external access - lua_pushliteral( L, "Linda"); - lua_setfield( L, -2, "__metatable"); + // protect metatable from external access + lua_pushliteral( L, "Linda"); + lua_setfield( L, -2, "__metatable"); - lua_pushcfunction( L, LG_linda_tostring); - lua_setfield( L, -2, "__tostring"); + lua_pushcfunction( L, LG_linda_tostring); + lua_setfield( L, -2, "__tostring"); - // Decoda __towatch support - lua_pushcfunction( L, LG_linda_dump); - lua_setfield( L, -2, "__towatch"); + // Decoda __towatch support + lua_pushcfunction( L, LG_linda_dump); + lua_setfield( L, -2, "__towatch"); - lua_pushcfunction( L, LG_linda_concat); - lua_setfield( L, -2, "__concat"); + lua_pushcfunction( L, LG_linda_concat); + lua_setfield( L, -2, "__concat"); - // [-1]: linda metatable - lua_pushcfunction( L, LG_linda_send); - lua_setfield( L, -2, "send"); + // [-1]: linda metatable + lua_pushcfunction( L, LG_linda_send); + lua_setfield( L, -2, "send"); - lua_pushcfunction( L, LG_linda_receive); - lua_setfield( L, -2, "receive"); + lua_pushcfunction( L, LG_linda_receive); + lua_setfield( L, -2, "receive"); - lua_pushcfunction( L, LG_linda_limit); - lua_setfield( L, -2, "limit"); + lua_pushcfunction( L, LG_linda_limit); + lua_setfield( L, -2, "limit"); - lua_pushcfunction( L, LG_linda_set); - lua_setfield( L, -2, "set"); + lua_pushcfunction( L, LG_linda_set); + lua_setfield( L, -2, "set"); - lua_pushcfunction( L, LG_linda_count); - lua_setfield( L, -2, "count"); + lua_pushcfunction( L, LG_linda_count); + lua_setfield( L, -2, "count"); - lua_pushcfunction( L, LG_linda_get); - lua_setfield( L, -2, "get"); + lua_pushcfunction( L, LG_linda_get); + lua_setfield( L, -2, "get"); - lua_pushcfunction( L, LG_linda_cancel); - lua_setfield( L, -2, "cancel"); + lua_pushcfunction( L, LG_linda_cancel); + lua_setfield( L, -2, "cancel"); - lua_pushcfunction( L, LG_linda_deep); - lua_setfield( L, -2, "deep"); + lua_pushcfunction( L, LG_linda_deep); + lua_setfield( L, -2, "deep"); - lua_pushcfunction( L, LG_linda_dump); - lua_setfield( L, -2, "dump"); + lua_pushcfunction( L, LG_linda_dump); + lua_setfield( L, -2, "dump"); - lua_pushliteral( L, BATCH_SENTINEL); - lua_setfield(L, -2, "batched"); + lua_pushliteral( L, BATCH_SENTINEL); + lua_setfield(L, -2, "batched"); - STACK_END( L, 1); - } - else if( strcmp( which, "module") == 0) - { + STACK_END( L, 1); + return NULL; + } + + case eDO_module: // linda is a special case because we know lanes must be loaded from the main lua state // to be able to ever get here, so we know it will remain loaded as long a the main state is around // in other words, forever. - lua_pushnil( L); - // other idfuncs must push a string naming the module they come from - //lua_pushliteral( L, "lanes.core"); + default: + { + return NULL; + } } } @@ -1214,7 +1219,7 @@ LUAG_FUNC( linda) luaL_argcheck( L, top <= 1, top, "too many arguments"); if( top == 1) luaL_checktype( L, 1, LUA_TSTRING); - return luaG_deep_userdata( L, linda_id); + return luaG_newdeepuserdata( L, linda_id); } /* @@ -2199,7 +2204,7 @@ LUAG_FUNC( thread_new) // which might not be the case if the libs list didn't include lanes.core or "*" if( strncmp( name, "lanes.core", len) == 0) // this works both both "lanes" and "lanes.core" because of len { - luaG_copy_one_time_settings( L, L2, name); + luaG_copy_one_time_settings( L, L2); } lua_pushlstring( L2, name, len); // require() name if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode @@ -2324,8 +2329,8 @@ LUAG_FUNC( thread_new) MUTEX_INIT( &s->done_lock); SIGNAL_INIT( &s->done_signal); #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR - s->mstatus= NORMAL; - s->selfdestruct_next= NULL; + s->mstatus = NORMAL; + s->selfdestruct_next = NULL; #if HAVE_LANE_TRACKING s->tracking_next = NULL; #endif // HAVE_LANE_TRACKING @@ -2969,7 +2974,7 @@ static void init_once_LOCKED( lua_State* L) // proxy_ud= deep_userdata( idfunc ) // lua_pushliteral( L, "lanes-timer"); // push a name for debug purposes - luaG_deep_userdata( L, linda_id); + luaG_newdeepuserdata( L, linda_id); STACK_MID( L, 2); lua_remove( L, -2); // remove the name as we no longer need it @@ -2978,7 +2983,7 @@ static void init_once_LOCKED( lua_State* L) // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer // timer_deep = * (DEEP_PRELUDE**) lua_touserdata( L, -1); - ASSERT_L( timer_deep && (timer_deep->refcount == 1) && timer_deep->deep); + ASSERT_L( timer_deep && (timer_deep->refcount == 1) && timer_deep->deep && timer_deep->idfunc == linda_id); // The host Lua state must always have a reference to this Linda object in order for the timer_deep pointer to be valid. // So store a reference that we will never actually use. @@ -3019,7 +3024,7 @@ LUAG_FUNC( configure) DEBUGSPEW_CODE( ++ debugspew_indent_depth); // not in init_once_LOCKED because we can have several hosted "master" Lua states where Lanes is require()d. - lua_getfield( L, 1, "protect_allocator"); // settings protect_allocator + lua_getfield( L, 1, "protect_allocator"); // settings protect_allocator if( lua_toboolean( L, -1)) { void* ud; @@ -3033,7 +3038,7 @@ LUAG_FUNC( configure) lua_setallocf( L, protected_lua_Alloc, s); } } - lua_pop( L, 1); // settings + lua_pop( L, 1); // settings STACK_MID( L, 0); /* @@ -3075,68 +3080,75 @@ LUAG_FUNC( configure) #endif // THREADAPI == THREADAPI_PTHREAD // Retrieve main module interface table - lua_pushvalue( L, lua_upvalueindex( 2)); // settings M + lua_pushvalue( L, lua_upvalueindex( 2)); // settings M // remove configure() (this function) from the module interface - lua_pushnil( L); // settings M nil - lua_setfield( L, -2, "configure"); // settings M + lua_pushnil( L); // settings M nil + lua_setfield( L, -2, "configure"); // settings M // add functions to the module's table luaG_registerlibfuncs( L, lanes_functions); #if HAVE_LANE_TRACKING // register core.threads() only if settings say it should be available if( tracking_first != NULL) { - lua_pushcfunction( L, LG_threads); // settings M LG_threads() - lua_setfield( L, -2, "threads"); + lua_pushcfunction( L, LG_threads); // settings M LG_threads() + lua_setfield( L, -2, "threads"); // settings M } #endif // HAVE_LANE_TRACKING STACK_MID( L, 1); - ASSERT_L( timer_deep != NULL); // initialized by init_once_LOCKED - luaG_push_proxy( L, linda_id, (DEEP_PRELUDE*) timer_deep); // settings M timer_deep - lua_setfield( L, -2, "timer_gateway"); // settings M + { + char const* errmsg; + ASSERT_L( timer_deep != NULL); // initialized by init_once_LOCKED + errmsg = push_deep_proxy( L, (DEEP_PRELUDE*) timer_deep, eLM_LaneBody); // settings M timer_deep + if( errmsg != NULL) + { + luaL_error( L, errmsg); + } + lua_setfield( L, -2, "timer_gateway"); // settings M + } STACK_MID( L, 1); // prepare the metatable for threads // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } // - if( luaL_newmetatable( L, "Lane")) // settings M mt + if( luaL_newmetatable( L, "Lane")) // settings M mt { - lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc - lua_setfield( L, -2, "__gc"); // settings M mt - lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index - lua_setfield( L, -2, "__index"); // settings M mt - lua_getglobal( L, "error"); // settings M mt error + lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc + lua_setfield( L, -2, "__gc"); // settings M mt + lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index + lua_setfield( L, -2, "__index"); // settings M mt + lua_getglobal( L, "error"); // settings M mt error ASSERT_L( lua_isfunction( L, -1)); - lua_setfield( L, -2, "cached_error"); // settings M mt - lua_getglobal( L, "tostring"); // settings M mt tostring + lua_setfield( L, -2, "cached_error"); // settings M mt + lua_getglobal( L, "tostring"); // settings M mt tostring ASSERT_L( lua_isfunction( L, -1)); - lua_setfield( L, -2, "cached_tostring"); // settings M mt - lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join - lua_setfield( L, -2, "join"); // settings M mt - lua_pushcfunction( L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname - lua_setfield( L, -2, "get_debug_threadname"); // settings M mt - lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel - lua_setfield( L, -2, "cancel"); // settings M mt - lua_pushliteral( L, "Lane"); // settings M mt "Lane" - lua_setfield( L, -2, "__metatable"); // settings M mt + lua_setfield( L, -2, "cached_tostring"); // settings M mt + lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join + lua_setfield( L, -2, "join"); // settings M mt + lua_pushcfunction( L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname + lua_setfield( L, -2, "get_debug_threadname"); // settings M mt + lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel + lua_setfield( L, -2, "cancel"); // settings M mt + lua_pushliteral( L, "Lane"); // settings M mt "Lane" + lua_setfield( L, -2, "__metatable"); // settings M mt } - lua_pushcclosure( L, LG_thread_new, 1); // settings M LG_thread_new - lua_setfield( L, -2, "thread_new"); // settings M + lua_pushcclosure( L, LG_thread_new, 1); // settings M LG_thread_new + lua_setfield( L, -2, "thread_new"); // settings M // we can't register 'lanes.require' normally because we want to create an upvalued closure - lua_getglobal( L, "require"); // settings M require - lua_pushcclosure( L, LG_require, 1); // settings M lanes.require - lua_setfield( L, -2, "require"); // settings M + lua_getglobal( L, "require"); // settings M require + lua_pushcclosure( L, LG_require, 1); // settings M lanes.require + lua_setfield( L, -2, "require"); // settings M - lua_pushstring(L, VERSION); // settings M VERSION - lua_setfield(L, -2, "version"); // settings M + lua_pushstring(L, VERSION); // settings M VERSION + lua_setfield(L, -2, "version"); // settings M - lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX - lua_setfield(L, -2, "max_prio"); // settings M + lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX + lua_setfield(L, -2, "max_prio"); // settings M - lua_pushlightuserdata( L, CANCEL_ERROR); // settings M CANCEL_ERROR - lua_setfield(L, -2, "cancel_error"); // settings M + lua_pushlightuserdata( L, CANCEL_ERROR); // settings M CANCEL_ERROR + lua_setfield(L, -2, "cancel_error"); // settings M // register all native functions found in that module in the transferable functions database // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) @@ -3151,7 +3163,7 @@ LUAG_FUNC( configure) // set _R[CONFIG_REGKEY] = settings lua_pushvalue( L, -2); // settings M settings lua_setfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); // settings M - lua_pop( L, 1); // settings + lua_pop( L, 1); // settings STACK_END( L, 0); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L)); DEBUGSPEW_CODE( -- debugspew_indent_depth); diff --git a/src/tools.c b/src/tools.c index fc3e7e0..2aa9b82 100644 --- a/src/tools.c +++ b/src/tools.c @@ -195,14 +195,14 @@ int initialize_on_state_create( lua_State* L) } // just like lua_xmove, args are (from, to) -void luaG_copy_one_time_settings( lua_State* L, lua_State* L2, char const* name_) +void luaG_copy_one_time_settings( lua_State* L, lua_State* L2) { STACK_GROW( L, 1); // copy settings from from source to destination registry lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); if( luaG_inter_move( L, L2, 1, eLM_LaneBody) < 0) // error? { - (void) luaL_error( L, "failed to copy settings when loading %s", name_); + (void) luaL_error( L, "failed to copy settings when loading lanes.core"); } lua_setfield( L2, LUA_REGISTRYINDEX, CONFIG_REGKEY); } @@ -258,7 +258,7 @@ static void open1lib( lua_State* L, char const* name_, size_t len_, lua_State* f if( isLanesCore == TRUE) { // copy settings from from source to destination registry - luaG_copy_one_time_settings( from_, L, name_); + luaG_copy_one_time_settings( from_, L); } // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack) luaL_requiref( L, name_, libfunc, !isLanesCore); @@ -591,28 +591,61 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) DEBUGSPEW_CODE( -- debugspew_indent_depth); } +void call_on_state_create( lua_State* L, lua_State* from_, enum eLookupMode mode_) +{ + if( s_on_state_create_func != NULL) + { + STACK_CHECK( L); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); + if( s_on_state_create_func != initialize_on_state_create) + { + // C function: recreate a closure in the new state, bypassing the lookup scheme + lua_pushcfunction( L, s_on_state_create_func); + } + else // Lua function located in the config table, copied when we opened "lanes.core" + { + if( mode_ != eLM_LaneBody) + { + // if attempting to call in a keeper state, do nothing because the function doesn't exist there + return; + } + lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); + lua_getfield( L, -1, "on_state_create"); + lua_remove( L, -2); + } + // capture error and forward it to main state + if( lua_pcall( L, 0, 0, 0) != LUA_OK) + { + (void) luaL_error( from_, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1))); + } + STACK_END( L, 0); + } +} + /* -* Like 'luaL_openlibs()' but allows the set of libraries be selected -* -* NULL no libraries, not even base -* "" base library only -* "io,string" named libraries -* "*" all libraries -* -* Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL. -* -*/ -lua_State* luaG_newstate( lua_State* _from, char const* libs) + * Like 'luaL_openlibs()' but allows the set of libraries be selected + * + * NULL no libraries, not even base + * "" base library only + * "io,string" named libraries + * "*" all libraries + * + * Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL. + * + * *NOT* called for keeper states! + * + */ +lua_State* luaG_newstate( lua_State* from_, char const* libs_) { // reuse alloc function from the originating state #if PROPAGATE_ALLOCF - PROPAGATE_ALLOCF_PREP( _from); + PROPAGATE_ALLOCF_PREP( from_); #endif // PROPAGATE_ALLOCF lua_State* L = PROPAGATE_ALLOCF_ALLOC(); if( L == NULL) { - (void) luaL_error( _from, "luaG_newstate() failed while creating state; out of memory"); + (void) luaL_error( from_, "luaG_newstate() failed while creating state; out of memory"); } // we'll need this everytime we transfer some C function from/to this state @@ -620,7 +653,7 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // neither libs (not even 'base') nor special init func: we are done - if( libs == NULL && s_on_state_create_func == NULL) + if( libs_ == NULL && s_on_state_create_func == NULL) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(NULL)\n" INDENT_END)); return L; @@ -636,17 +669,17 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) // Anything causes 'base' to be taken in // - if( libs != NULL) + if( libs_ != NULL) { // special "*" case (mainly to help with LuaJIT compatibility) // as we are called from luaopen_lanes_core() already, and that would deadlock - if( libs[0] == '*' && libs[1] == 0) + if( libs_[0] == '*' && libs_[1] == 0) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END)); luaL_openlibs( L); // don't forget lanes.core for regular lane states - open1lib( L, "lanes.core", 10, _from); - libs = NULL; // done with libs + open1lib( L, "lanes.core", 10, from_); + libs_ = NULL; // done with libs } else { @@ -665,11 +698,11 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) STACK_END( L, 0); // scan all libraries, open them one by one - if( libs) + if( libs_) { char const* p; unsigned int len = 0; - for( p = libs; *p; p += len) + for( p = libs_; *p; p += len) { // skip delimiters ('.' can be part of name for "lanes.core") while( *p && !isalnum( *p) && *p != '.') @@ -679,37 +712,17 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) while( isalnum( p[len]) || p[len] == '.') ++ len; // open library - open1lib( L, p, len, _from); + open1lib( L, p, len, from_); } serialize_require( L); } lua_gc( L, LUA_GCRESTART, 0); - STACK_CHECK( L); // call this after the base libraries are loaded and GC is restarted - if( s_on_state_create_func != NULL) - { - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); - if( s_on_state_create_func != initialize_on_state_create) - { - // C function: recreate a closure in the new state, bypassing the lookup scheme - lua_pushcfunction( L, s_on_state_create_func); - } - else // Lua function located in the config table - { - lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); - lua_getfield( L, -1, "on_state_create"); - lua_remove( L, -2); - } - // capture error and forward it to main state - if( lua_pcall( L, 0, 0, 0) != LUA_OK) - { - (void) luaL_error( _from, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1))); - } - STACK_MID( L, 0); - } + call_on_state_create( L, from_, eLM_LaneBody); + STACK_CHECK( L); // after all this, register everything we find in our name<->function database lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack populate_func_lookup_table( L, -1, NULL); @@ -743,7 +756,7 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) /* * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying */ -#define DEEP_PROXY_CACHE_KEY ((void*)luaG_push_proxy) +#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy) static void push_registry_subtable_mode( lua_State *L, void *token, const char* mode ); static void push_registry_subtable( lua_State *L, void *token ); @@ -752,107 +765,76 @@ static void push_registry_subtable( lua_State *L, void *token ); * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. * Pops the both values off the stack. */ -void set_deep_lookup( lua_State *L ) { - - STACK_GROW(L,3); - - STACK_CHECK( L); -#if 1 - push_registry_subtable( L, DEEP_LOOKUP_KEY ); -#else - /* ..to be removed.. */ - lua_pushlightuserdata( L, DEEP_LOOKUP_KEY ); - lua_rawget( L, LUA_REGISTRYINDEX ); - - if (lua_isnil(L,-1)) { - // First time here; let's make the lookup - // - lua_pop(L,1); - - lua_newtable(L); - lua_pushlightuserdata( L, DEEP_LOOKUP_KEY ); - lua_pushvalue(L,-2); - // - // [-3]: {} (2nd ref) - // [-2]: DEEP_LOOKUP_KEY - // [-1]: {} - - lua_rawset( L, LUA_REGISTRYINDEX ); - // - // [-1]: lookup table (empty) - } -#endif - STACK_MID( L, 1); - - lua_insert(L,-3); - - // [-3]: lookup table - // [-2]: A - // [-1]: B - - lua_pushvalue( L,-1 ); // B - lua_pushvalue( L,-3 ); // A - lua_rawset( L, -5 ); // B->A - lua_rawset( L, -3 ); // A->B - lua_pop( L,1 ); - - STACK_END( L, -2); +static void set_deep_lookup( lua_State* L) +{ + STACK_GROW( L, 3); + STACK_CHECK( L); // a b + push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {} + STACK_MID( L, 1); + lua_insert( L, -3); // {} a b + lua_pushvalue( L, -1); // {} a b b + lua_pushvalue( L,-3); // {} a b b a + lua_rawset( L, -5); // {} a b + lua_rawset( L, -3); // {} + lua_pop( L, 1); // + STACK_END( L, -2); } /* * Pops the key (metatable or idfunc) off the stack, and replaces with the * deep lookup value (idfunc/metatable/nil). */ -void get_deep_lookup( lua_State *L ) { - - STACK_GROW(L,1); +static void get_deep_lookup( lua_State* L) +{ + STACK_GROW( L, 1); + STACK_CHECK( L); // a + lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK + lua_rawget( L, LUA_REGISTRYINDEX); // a {} - STACK_CHECK( L); - lua_pushlightuserdata( L, DEEP_LOOKUP_KEY ); - lua_rawget( L, LUA_REGISTRYINDEX ); - - if (!lua_isnil(L,-1)) { - // [-2]: key (metatable or idfunc) - // [-1]: lookup table - - lua_insert( L, -2 ); - lua_rawget( L, -2 ); - - // [-2]: lookup table - // [-1]: value (metatable / idfunc / nil) - } - lua_remove(L,-2); - // remove lookup, or unused key - STACK_END( L, 0); + if( !lua_isnil( L, -1)) + { + lua_insert( L, -2); // {} a + lua_rawget( L, -2); // {} b + } + lua_remove( L, -2); // a|b + STACK_END( L, 0); } /* * Return the registered ID function for 'index' (deep userdata proxy), * or NULL if 'index' is not a deep userdata proxy. */ -static -luaG_IdFunction get_idfunc( lua_State *L, int index ) +static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_) { - luaG_IdFunction ret; - - index = lua_absindex( L, index); - - STACK_GROW(L,1); - - STACK_CHECK( L); - if (!lua_getmetatable( L, index )) - return NULL; // no metatable - - // [-1]: metatable of [index] + // when looking inside a keeper, we are 100% sure the object is a deep userdata + if( mode_ == eLM_FromKeeper) + { + DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index); + // we can (and must) cast and fetch the internally stored idfunc + return (*proxy)->idfunc; + } + else + { + // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database + // it is the only way to ensure that the userdata is indeed a deep userdata! + // of course, we could just trust the caller, but we won't + luaG_IdFunction ret; + STACK_GROW( L, 1); + STACK_CHECK( L); + + if( !lua_getmetatable( L, index)) // deep ... metatable? + { + return NULL; // no metatable: can't be a deep userdata object! + } - get_deep_lookup(L); - // - // [-1]: idfunc/nil + // replace metatable with the idfunc pointer, if it is actually a deep userdata + get_deep_lookup( L); // deep ... idfunc|nil - ret= (luaG_IdFunction)lua_touserdata(L,-1); - lua_pop(L,1); - STACK_END( L, 0); - return ret; + ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata + lua_pop( L, 1); + STACK_END( L, 0); + return ret; + } } @@ -862,205 +844,190 @@ luaG_IdFunction get_idfunc( lua_State *L, int index ) * End of life for a proxy object; reduce the deep reference count and clean * it up if reaches 0. */ -static -int deep_userdata_gc( lua_State *L ) +static int deep_userdata_gc( lua_State* L) { - DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 ); - DEEP_PRELUDE *p= *proxy; - int v; + DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1); + DEEP_PRELUDE* p = *proxy; + int v; - *proxy= 0; // make sure we don't use it any more + *proxy = 0; // make sure we don't use it any more - MUTEX_LOCK( &deep_lock ); - v= --(p->refcount); - MUTEX_UNLOCK( &deep_lock ); - - if (v==0) - { - // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup - // - luaG_IdFunction idfunc = get_idfunc(L,1); - ASSERT_L(idfunc); - - lua_settop( L, 0); // clean stack so we can call 'idfunc' directly + MUTEX_LOCK( &deep_lock); + v = -- (p->refcount); + MUTEX_UNLOCK( &deep_lock); - // void= idfunc( "delete", lightuserdata ) - // - lua_pushlightuserdata( L, p->deep ); - idfunc( L, "delete"); - - // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! - if ( lua_gettop( L) > 1) - luaL_error( L, "Bad idfunc on \"delete\": returned something"); - - DEEP_FREE( (void*)p ); - } - return 0; + if( v == 0) + { + // clean stack so we can call 'idfunc' directly + lua_settop( L, 0); + // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup + lua_pushlightuserdata( L, p->deep); + ASSERT_L( p->idfunc); + p->idfunc( L, eDO_delete); + DEEP_FREE( (void*) p); + + // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! + if ( lua_gettop( L) > 1) + { + luaL_error( L, "Bad idfunc(eDO_delete): should not push anything"); + } + } + return 0; } /* -* Push a proxy userdata on the stack. -* -* Initializes necessary structures if it's the first time 'idfunc' is being -* used in this Lua state (metatable, registring it). Otherwise, increments the -* reference count. -*/ -void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *prelude ) + * Push a proxy userdata on the stack. + * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem + * (error cannot happen with mode_ == eLM_ToKeeper) + * + * Initializes necessary structures if it's the first time 'idfunc' is being + * used in this Lua state (metatable, registring it). Otherwise, increments the + * reference count. + */ +char const* push_deep_proxy( lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_) { - DEEP_PRELUDE **proxy; - - // Check if a proxy already exists - push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); - lua_pushlightuserdata(L, prelude->deep); - lua_rawget(L, -2); - if (!lua_isnil(L, -1)) - { - lua_remove(L, -2); // deep proxy cache table - return; - } - else - { - lua_pop(L, 2); // Pop the nil and proxy cache table - } - - MUTEX_LOCK( &deep_lock ); - ++(prelude->refcount); // one more proxy pointing to this deep data - MUTEX_UNLOCK( &deep_lock ); - - STACK_GROW(L,4); - - STACK_CHECK( L); - - proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) ); - ASSERT_L(proxy); - *proxy= prelude; + DEEP_PRELUDE** proxy; - // Get/create metatable for 'idfunc' (in this state) - // - lua_pushlightuserdata( L, idfunc ); // key - get_deep_lookup(L); - // - // [-2]: proxy - // [-1]: metatable / nil - - if (lua_isnil(L,-1)) - { - // No metatable yet. We have two things to do: - // 1 - make one and register it - { - int oldtop; + // Check if a proxy already exists + push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC + lua_pushlightuserdata( L, prelude->deep); // DPC deep + lua_rawget( L, -2); // DPC proxy + if ( !lua_isnil( L, -1)) + { + lua_remove( L, -2); // proxy + return NULL; + } + else + { + lua_pop( L, 1); // DPC + } - lua_pop( L, 1); + MUTEX_LOCK( &deep_lock); + ++ (prelude->refcount); // one more proxy pointing to this deep data + MUTEX_UNLOCK( &deep_lock); - // tbl= idfunc( "metatable" ) - // - oldtop = lua_gettop( L); - idfunc( L, "metatable"); - // - // [-2]: proxy - // [-1]: metatable (returned by 'idfunc') + STACK_GROW( L, 7); + STACK_CHECK( L); - if (lua_gettop( L) - oldtop != 1 || !lua_istable(L, -1)) - { - luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); - } + proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy + ASSERT_L( proxy); + *proxy = prelude; - // Add '__gc' method - // - lua_pushcfunction( L, deep_userdata_gc ); - lua_setfield( L, -2, "__gc" ); + // Get/create metatable for 'idfunc' (in this state) + lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc + get_deep_lookup( L); // DPC proxy metatable? - // Memorize for later rounds - // - lua_pushvalue( L,-1 ); - lua_pushlightuserdata( L, idfunc ); - // - // [-4]: proxy - // [-3]: metatable (2nd ref) - // [-2]: metatable - // [-1]: idfunc - - set_deep_lookup(L); - } - - // 2 - cause the target state to require the module that exported the idfunc - // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc - STACK_CHECK( L); - { - char const * modname; - // make sure the function pushed a single value on the stack! - { - int oldtop = lua_gettop( L); - idfunc( L, "module"); // ... "module"/nil - if( lua_gettop( L) - oldtop != 1) - { - luaL_error( L, "Bad idfunc on \"module\": should return a single value"); - } - } - modname = luaL_optstring( L, -1, NULL); // raises an error if not a string or nil - if( modname) // we actually got a module name - { - // somehow, L.registry._LOADED can exist without having registered the 'package' library. - lua_getglobal( L, "require"); // ... "module" require() - // check that the module is already loaded (or being loaded, we are happy either way) - if( lua_isfunction( L, -1)) - { - lua_insert( L, -2); // ... require() "module" - lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // ... require() "module" L.registry._LOADED - if( lua_istable( L, -1)) - { - bool_t alreadyloaded; - lua_pushvalue( L, -2); // ... require() "module" L.registry._LOADED "module" - lua_rawget( L, -2); // ... require() "module" L.registry._LOADED module - alreadyloaded = lua_toboolean( L, -1); - if( !alreadyloaded) // not loaded - { - lua_pop( L, 2); // ... require() "module" - lua_call( L, 1, 0); // call require "modname" // ... - } - else // already loaded, we are happy - { - lua_pop( L, 4); // ... - } - } - else // no L.registry._LOADED; can this ever happen? - { - luaL_error( L, "unexpected error while requiring a module"); - lua_pop( L, 3); // ... - } - } - else // a module name, but no require() function :-( - { - luaL_error( L, "lanes receiving deep userdata should register the 'package' library"); - lua_pop( L, 2); // ... - } - } - else // no module name - { - lua_pop( L, 1); // ... - } - } - STACK_END( L, 0); - } - STACK_MID( L, 2); - ASSERT_L( lua_isuserdata(L,-2) ); - ASSERT_L( lua_istable(L,-1) ); + if( lua_isnil( L, -1)) // // No metatable yet. + { + char const* modname; + int oldtop = lua_gettop( L); // DPC proxy nil + lua_pop( L, 1); // DPC proxy + // 1 - make one and register it + if( mode_ != eLM_ToKeeper) + { + prelude->idfunc( L, eDO_metatable); // DPC proxy metatable + if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1)) + { + lua_pop( L, 3); // + return "Bad idfunc(eOP_metatable): unexpected pushed value"; + } + // make sure the idfunc didn't export __gc, as we will store our own + lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc + if( !lua_isnil( L, -1)) + { + lua_pop( L, 4); // + return "idfunc-created metatable shouldn't contain __gc"; + } + lua_pop( L, 1); // DPC proxy metatable + } + else + { + // keepers need a minimal metatable that only contains __gc + lua_newtable( L); // DPC proxy metatable + } + // Add our own '__gc' method + lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc + lua_setfield( L, -2, "__gc"); // DPC proxy metatable - // [-2]: proxy userdata - // [-1]: metatable to use + // Memorize for later rounds + lua_pushvalue( L, -1); // DPC proxy metatable metatable + lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc + set_deep_lookup( L); // DPC proxy metatable - lua_setmetatable( L, -2 ); - - // If we're here, we obviously had to create a new proxy, so cache it. - push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); - lua_pushlightuserdata(L, (*proxy)->deep); - lua_pushvalue(L, -3); // Copy of the proxy - lua_rawset(L, -3); - lua_pop(L, 1); // Remove the cache proxy table - - STACK_END( L, 1); - // [-1]: proxy userdata + // 2 - cause the target state to require the module that exported the idfunc + // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc + { + int oldtop = lua_gettop( L); + modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable + // make sure the function pushed nothing on the stack! + if( lua_gettop( L) - oldtop != 0) + { + lua_pop( L, 3); // + return "Bad idfunc(eOP_module): should not push anything"; + } + } + if( modname) // we actually got a module name + { + // somehow, L.registry._LOADED can exist without having registered the 'package' library. + lua_getglobal( L, "require"); // DPC proxy metatable require() + // check that the module is already loaded (or being loaded, we are happy either way) + if( lua_isfunction( L, -1)) + { + lua_pushstring( L, modname); // DPC proxy metatable require() "module" + lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED + if( lua_istable( L, -1)) + { + bool_t alreadyloaded; + lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module" + lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module + alreadyloaded = lua_toboolean( L, -1); + if( !alreadyloaded) // not loaded + { + int require_result; + lua_pop( L, 2); // DPC proxy metatable require() "module" + // require "modname" + require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error? + if( require_result != LUA_OK) + { + // failed, return the error message + lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); + lua_insert( L, -2); // DPC proxy metatable prefix error + lua_concat( L, 2); // DPC proxy metatable error + return lua_tostring( L, -1); + } + } + else // already loaded, we are happy + { + lua_pop( L, 4); // DPC proxy metatable + } + } + else // no L.registry._LOADED; can this ever happen? + { + lua_pop( L, 6); // + return "unexpected error while requiring a module identified by idfunc(eOP_module)"; + } + } + else // a module name, but no require() function :-( + { + lua_pop( L, 4); // + return "lanes receiving deep userdata should register the 'package' library"; + } + } + } + STACK_MID( L, 2); // DPC proxy metatable + ASSERT_L( lua_isuserdata( L, -2)); + ASSERT_L( lua_istable( L, -1)); + lua_setmetatable( L, -2); // DPC proxy + + // If we're here, we obviously had to create a new proxy, so cache it. + lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep + lua_pushvalue( L, -2); // DPC proxy deep proxy + lua_rawset( L, -4); // DPC proxy + lua_remove( L, -2); // proxy + ASSERT_L( lua_isuserdata( L, -1)); + STACK_END( L, 0); + return NULL; } @@ -1086,39 +1053,34 @@ void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *prelud * * Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' */ -int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc) +int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) { - int oldtop; - - DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) ); - ASSERT_L(prelude); - - prelude->refcount= 0; // 'luaG_push_proxy' will lift it to 1 - - STACK_GROW(L,1); - STACK_CHECK( L); - - // lightuserdata= idfunc( "new" [, ...] ) - // - oldtop = lua_gettop( L); - idfunc(L, "new"); - - if( lua_gettop( L) - oldtop != 1 || lua_type( L, -1) != LUA_TLIGHTUSERDATA) - { - luaL_error( L, "Bad idfunc on \"new\": did not return light userdata"); - } + char const* errmsg; + DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE)); + ASSERT_L( prelude); - prelude->deep= lua_touserdata(L,-1); - ASSERT_L(prelude->deep); + prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1 + prelude->idfunc = idfunc; - lua_pop(L,1); // pop deep data - - luaG_push_proxy( L, idfunc, prelude ); - // - // [-1]: proxy userdata + STACK_GROW( L, 1); + STACK_CHECK( L); + { + int oldtop = lua_gettop( L); + prelude->deep = idfunc( L, eDO_new); + ASSERT_L( prelude->deep); - STACK_END( L, 1); - return 1; + if( lua_gettop( L) - oldtop != 0) + { + luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack"); + } + } + errmsg = push_deep_proxy( L, prelude, eLM_LaneBody); // proxy + if( errmsg != NULL) + { + luaL_error( L, errmsg); + } + STACK_END( L, 1); + return 1; } @@ -1128,52 +1090,51 @@ int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc) * Reference count is not changed, and access to the deep userdata is not * serialized. It is the module's responsibility to prevent conflicting usage. */ -void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ) +void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) { - DEEP_PRELUDE **proxy; + DEEP_PRELUDE** proxy; - STACK_CHECK( L); - if( get_idfunc(L,index) != idfunc) - return NULL; // no metatable, or wrong kind + STACK_CHECK( L); + // ensure it is actually a deep userdata + if( get_idfunc( L, index, eLM_LaneBody) != idfunc) + { + return NULL; // no metatable, or wrong kind + } - proxy= (DEEP_PRELUDE**)lua_touserdata( L, index ); - STACK_END( L, 0); + proxy = (DEEP_PRELUDE**) lua_touserdata( L, index); + STACK_END( L, 0); - return (*proxy)->deep; + return (*proxy)->deep; } /* -* Copy deep userdata between two separate Lua states. -* -* Returns: -* the id function of the copied value, or NULL for non-deep userdata -* (not copied) -*/ -static -luaG_IdFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) + * Copy deep userdata between two separate Lua states (from L to L2) + * + * Returns: + * the id function of the copied value, or NULL for non-deep userdata + * (not copied) + */ +static luaG_IdFunction copydeep( lua_State* L, lua_State* L2, int index, enum eLookupMode mode_) { - DEEP_PRELUDE **proxy; - DEEP_PRELUDE *p; - - luaG_IdFunction idfunc = get_idfunc( L, index); - if (!idfunc) - return NULL; // not a deep userdata - - // Increment reference count - // - proxy= (DEEP_PRELUDE**)lua_touserdata( L, index ); - p= *proxy; - - luaG_push_proxy( L2, idfunc, p ); - // - // L2 [-1]: proxy userdata + char const* errmsg; + luaG_IdFunction idfunc = get_idfunc( L, index, mode_); + if( idfunc == NULL) + { + return NULL; // not a deep userdata + } - return idfunc; + errmsg = push_deep_proxy( L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_); + if( errmsg != NULL) + { + // raise the error in the proper state (not the keeper) + lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L; + luaL_error( errL, errmsg); + } + return idfunc; } - /*---=== Inter-state copying ===---*/ /*-- Metatable copying --*/ @@ -1190,51 +1151,45 @@ luaG_IdFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) /* * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it */ -static -void push_registry_subtable_mode( lua_State *L, void *token, const char* mode ) { +static void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_) +{ + STACK_GROW( L, 3); + STACK_CHECK( L); - STACK_GROW(L,3); + lua_pushlightuserdata( L, key_); // key + lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil - STACK_CHECK( L); - - lua_pushlightuserdata( L, token ); - lua_rawget( L, LUA_REGISTRYINDEX ); - // - // [-1]: nil/subtable - - if (lua_isnil(L,-1)) { - lua_pop(L,1); - lua_newtable(L); // value - lua_pushlightuserdata( L, token ); // key - lua_pushvalue(L,-2); - // - // [-3]: value (2nd ref) - // [-2]: key - // [-1]: value - - lua_rawset( L, LUA_REGISTRYINDEX ); - - // Set it's metatable if requested - if (mode) { - lua_newtable(L); - lua_pushliteral(L, "__mode"); - lua_pushstring(L, mode); - lua_rawset(L, -3); - lua_setmetatable(L, -2); - } - } - STACK_END( L, 1); + if( lua_isnil( L, -1)) + { + lua_pop( L, 1); // + lua_newtable( L); // {} + lua_pushlightuserdata( L, key_); // {} key + lua_pushvalue( L, -2); // {} key {} + + // _R[key_] = {} + lua_rawset( L, LUA_REGISTRYINDEX); // {} - ASSERT_L( lua_istable(L,-1) ); + // Set its metatable if requested + if( mode_) + { + lua_newtable( L); // {} mt + lua_pushliteral( L, "__mode"); // {} mt "__mode" + lua_pushstring( L, mode_); // {} mt "__mode" mode + lua_rawset( L, -3); // {} mt + lua_setmetatable( L, -2); // {} + } + } + STACK_END( L, 1); + ASSERT_L( lua_istable( L, -1)); } /* -* Push a registry subtable (keyed by unique 'token') onto the stack. +* Push a registry subtable (keyed by unique 'key_') onto the stack. * If the subtable does not exist, it is created and chained. */ -static -void push_registry_subtable( lua_State *L, void *token ) { - push_registry_subtable_mode(L, token, NULL); +static inline void push_registry_subtable( lua_State* L, void* key_) +{ + push_registry_subtable_mode( L, key_, NULL); } #define REG_MTID ( (void*) get_mt_id ) @@ -1864,12 +1819,10 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, enum eLookupMode mode_, char const* upName_) { bool_t ret = TRUE; - STACK_GROW( L2, 1); - STACK_CHECK( L2); - switch ( lua_type( L, i)) + switch( lua_type( L, i)) { /* Basic types allowed both as values, and as table keys */ @@ -1921,7 +1874,7 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u /* Allow only deep userdata entities to be copied across */ DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "USERDATA\n" INDENT_END)); - if( !luaG_copydeep( L, L2, i)) + if( !copydeep( L, L2, i, mode_)) { // Not a deep full userdata bool_t demote = FALSE; @@ -2129,7 +2082,6 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u } STACK_END( L2, ret ? 1 : 0); - return ret; } @@ -2225,7 +2177,8 @@ void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx, enum eLooku // package.loaders is renamed package.searchers in Lua 5.2 // but don't copy it anyway, as the function names change depending on the slot index! // users should provide an on_state_create function to setup custom loaders instead - char const* entries[] = { "path", "cpath", "preload"/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; + // don't copy package.preload in keeper states (they don't know how to translate functions) + char const* entries[] = { "path", "cpath", (mode_ == eLM_LaneBody) ? "preload" : NULL/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; for( i = 0; entries[i]; ++ i) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s\n" INDENT_END, entries[i])); diff --git a/src/tools.h b/src/tools.h index be358db..ebe407b 100644 --- a/src/tools.h +++ b/src/tools.h @@ -99,11 +99,15 @@ extern int debugspew_indent_depth; void luaG_dump( lua_State* L ); lua_State* luaG_newstate( lua_State* _from, char const* libs); -void luaG_copy_one_time_settings( lua_State* L, lua_State* L2, char const* name_); +void luaG_copy_one_time_settings( lua_State* L, lua_State* L2); -typedef struct { - volatile int refcount; - void *deep; +// this is pointed to by full userdata proxies, and allocated with malloc() to survive any lua_State lifetime +typedef struct +{ + volatile int refcount; + void* deep; + // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc + luaG_IdFunction idfunc; } DEEP_PRELUDE; enum eLookupMode @@ -113,7 +117,7 @@ enum eLookupMode eLM_FromKeeper // send a function from a keeper state to a lane }; -void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_userdata); +char const* push_deep_proxy( lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_); void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx, enum eLookupMode mode_); int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n, enum eLookupMode mode_); @@ -130,6 +134,8 @@ extern MUTEX_T mtid_lock; void populate_func_lookup_table( lua_State* L, int _i, char const* _name); void serialize_require( lua_State *L); int initialize_on_state_create( lua_State *L); +void call_on_state_create( lua_State* L, lua_State* from_, enum eLookupMode mode_); + extern MUTEX_T require_cs; // for verbose errors -- cgit v1.2.3-55-g6feb