From 77630de350fc89038378c798cd482ed751280fc2 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 17 Jun 2014 16:34:31 +0200 Subject: Deep userdata changes * bumped version to 3.9.6 * separate deep userdata code in a dedicated file to allow external modules to implement Lanes-compatible deep userdata without requiring a binary dependency against the Lanes module. because of this linda_id function(eDO_metatable) must push 2 values on the stack: a metatable and a deep version string obtained from luaG_pushdeepversion() --- src/tools.c | 472 +----------------------------------------------------------- 1 file changed, 4 insertions(+), 468 deletions(-) (limited to 'src/tools.c') diff --git a/src/tools.c b/src/tools.c index becf31e..628b277 100644 --- a/src/tools.c +++ b/src/tools.c @@ -44,6 +44,10 @@ THE SOFTWARE. #include #endif +// functions implemented in deep.c +extern luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_); +extern void push_registry_subtable( lua_State* L, void* key_); + void* const UNIVERSE_REGKEY = (void*) luaopen_lanes_core; /* @@ -708,477 +712,9 @@ lua_State* luaG_newstate( struct s_Universe* U, lua_State* from_, char const* li -/*---=== Deep userdata ===---*/ - -/* The deep portion must be allocated separately of any Lua state's; it's -* lifespan may be longer than that of the creating state. -*/ -#define DEEP_MALLOC malloc -#define DEEP_FREE free - -/* -* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's -* metatables: -* -* metatable -> idfunc -* idfunc -> metatable -*/ -#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup) - // any unique light userdata - - -/* -* 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*)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 ); - -/* -* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. -* Pops the both values off the stack. -*/ -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). -*/ -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 {} - - 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 inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_) -{ - // 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! - } - - // 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); // NULL if not a userdata - lua_pop( L, 1); - STACK_END( L, 0); - return ret; - } -} - - -void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_) -{ - // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup - lua_pushlightuserdata( L, prelude_->deep); - ASSERT_L( prelude_->idfunc); - prelude_->idfunc( L, eDO_delete); - DEEP_FREE( (void*) prelude_); -} - - -/* -* void= mt.__gc( proxy_ud ) -* -* 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) -{ - DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1); - DEEP_PRELUDE* p = *proxy; - struct s_Universe* U = get_universe( L); - int v; - - *proxy = 0; // make sure we don't use it any more - - MUTEX_LOCK( &U->deep_lock); - v = -- (p->refcount); - MUTEX_UNLOCK( &U->deep_lock); - - if( v == 0) - { - // 'idfunc' expects a clean stack to work on - lua_settop( L, 0); - free_deep_prelude( L, 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. - * 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( struct s_Universe* U, 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"); // 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 - } - - MUTEX_LOCK( &U->deep_lock); - ++ (prelude->refcount); // one more proxy pointing to this deep data - MUTEX_UNLOCK( &U->deep_lock); - - STACK_GROW( L, 7); - STACK_CHECK( L); - - proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy - ASSERT_L( proxy); - *proxy = prelude; - - // Get/create metatable for 'idfunc' (in this state) - lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc - get_deep_lookup( L); // DPC proxy metatable? - - 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 - - // 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 - - // 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; -} - - -/* -* Create a deep userdata -* -* proxy_ud= deep_userdata( idfunc [, ...] ) -* -* Creates a deep userdata entry of the type defined by 'idfunc'. -* Other parameters are passed on to the 'idfunc' "new" invocation. -* -* 'idfunc' must fulfill the following features: -* -* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance -* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance -* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies -* -* Reference counting and true userdata proxying are taken care of for the -* actual data type. -* -* Types using the deep userdata system (and only those!) can be passed between -* separate Lua states via 'luaG_inter_move()'. -* -* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' -*/ -int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) -{ - char const* errmsg; - DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE)); - if( prelude == NULL) - { - return luaL_error( L, "couldn't not allocate deep prelude: out of memory"); - } - - prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1 - prelude->idfunc = idfunc; - - STACK_GROW( L, 1); - STACK_CHECK( L); - { - int oldtop = lua_gettop( L); - prelude->deep = idfunc( L, eDO_new); - if( prelude->deep == NULL) - { - luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)"); - } - - if( lua_gettop( L) - oldtop != 0) - { - luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack"); - } - } - errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy - if( errmsg != NULL) - { - luaL_error( L, errmsg); - } - STACK_END( L, 1); - return 1; -} - - -/* -* Access deep userdata through a proxy. -* -* 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) -{ - DEEP_PRELUDE** proxy; - - 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); - - return (*proxy)->deep; -} - - -/* - * 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( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_) -{ - char const* errmsg; - luaG_IdFunction idfunc = get_idfunc( L, index, mode_); - if( idfunc == NULL) - { - return NULL; // not a deep userdata - } - - errmsg = push_deep_proxy( U, 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 --*/ - -/* - * 'reg[ REG_MT_KNOWN ]'= { - * [ table ]= id_uint, - * ... - * [ id_uint ]= table, - * ... - * } - */ - -/* -* 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* key_, const char* mode_) -{ - STACK_GROW( L, 3); - STACK_CHECK( L); - - lua_pushlightuserdata( L, key_); // key - lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil - - 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); // {} - - // 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 'key_') onto the stack. -* If the subtable does not exist, it is created and chained. -*/ -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 ) /* -- cgit v1.2.3-55-g6feb