From 47eb3f94373a13ac9f204ca65dfde602f53bdc1a Mon Sep 17 00:00:00 2001 From: Benoit Germain <bnt period germain arrobase gmail period com> 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/lanes.c | 286 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 149 insertions(+), 137 deletions(-) (limited to 'src/lanes.c') 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); -- cgit v1.2.3-55-g6feb