From 68d8db431ec2b739dc53233d6b4d8aeee9324e48 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Thu, 24 Jan 2013 22:46:21 +0100 Subject: version 3.4.3 * raise an error if lane generator libs specification contains a lib more than once * bit32 is a valid lib name in the libs specification (silently ignored by the Lua 5.1 build) * improved lanes.nameof to search inside table- and userdata- metatables for an object's name * fixed an unwarranted error when trying to discover a function name upon a failed transfer * contents of package.[path,cpath,preload,loaders|searchers] are pulled *only once* inside keeper states at initialisation * Lua function upvalues equal to the global environment aren't copied by value, but bound to the destination's global environment especially useful for Lua 5.2 _ENV * fixed loading of base libraries that didn't create the global tables when built for Lua 5.2 --- src/keeper.c | 57 ++++----- src/keeper.h | 2 +- src/lanes.c | 371 +++++++++++++++++++++++++++++++++------------------------- src/lanes.lua | 18 ++- src/tools.c | 249 ++++++++++++++++++++++++++------------- src/tools.h | 20 +++- 6 files changed, 428 insertions(+), 289 deletions(-) (limited to 'src') diff --git a/src/keeper.c b/src/keeper.c index 0be4fd3..a7c8647 100644 --- a/src/keeper.c +++ b/src/keeper.c @@ -557,7 +557,7 @@ void close_keepers( void) * unclosed, because it does not really matter. In production code, this * function never fails. */ -char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create) +char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create) { int i; assert( _nbKeepers >= 1); @@ -565,15 +565,16 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create) GKeepers = malloc( _nbKeepers * sizeof( struct s_Keeper)); for( i = 0; i < _nbKeepers; ++ i) { - // We need to load all base libraries in the keeper states so that the transfer databases are populated properly // // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs // the others because they export functions that we may store in a keeper for transfer between lanes lua_State* K = luaG_newstate( "*", _on_state_create); - if (!K) + if( !K) return "out of memory"; + DEBUGSPEW_CODE( fprintf( stderr, "init_keepers %d\n", i)); + STACK_CHECK( K) // to see VM name in Decoda debugger lua_pushliteral( K, "Keeper #"); @@ -581,6 +582,11 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create) lua_concat( K, 2); lua_setglobal( K, "decoda_name"); + // replace default 'package' contents with stuff gotten from the master state + lua_getglobal( L, "package"); + luaG_inter_copy_package( L, K, -1); + lua_pop( L, 1); + #if KEEPER_MODEL == KEEPER_MODEL_C // create the fifos table in the keeper state lua_pushlightuserdata( K, fifos_key); @@ -589,7 +595,7 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create) #endif // KEEPER_MODEL == KEEPER_MODEL_C #if KEEPER_MODEL == KEEPER_MODEL_LUA - // use package.loaders[2] to find keeper microcode + // use package.loaders[2] to find keeper microcode (NOTE: this works only if nobody tampered with the loaders table...) lua_getglobal( K, "package"); // package lua_getfield( K, -1, "loaders"); // package package.loaders lua_rawgeti( K, -1, 2); // package package.loaders package.loaders[2] @@ -619,55 +625,38 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create) } // cause each keeper state to populate its database of transferable functions with those from the specified module -void populate_keepers( lua_State *L) +// do do this we simply require the module inside the keeper state, then populate the lookup database +void populate_keepers( lua_State* L) { size_t name_len; - char const *name = luaL_checklstring( L, -1, &name_len); - size_t package_path_len; - char const *package_path; - size_t package_cpath_len; - char const *package_cpath; + char const* name = luaL_checklstring( L, -1, &name_len); int i; - // we need to make sure that package.path & package.cpath are the same in the keepers -// than what is currently in use when the module is required in the caller's Lua state - STACK_CHECK(L) + STACK_CHECK( L) STACK_GROW( L, 3); - lua_getglobal( L, "package"); - lua_getfield( L, -1, "path"); - package_path = luaL_checklstring( L, -1, &package_path_len); - lua_getfield( L, -2, "cpath"); - package_cpath = luaL_checklstring( L, -1, &package_cpath_len); for( i = 0; i < GNbKeepers; ++ i) { - lua_State *K = GKeepers[i].L; + lua_State* K = GKeepers[i].L; int res; MUTEX_LOCK( &GKeepers[i].lock_); - STACK_CHECK(K) + STACK_CHECK( K) STACK_GROW( K, 2); - lua_getglobal( K, "package"); - lua_pushlstring( K, package_path, package_path_len); - lua_setfield( K, -2, "path"); - lua_pushlstring( K, package_cpath, package_cpath_len); - lua_setfield( K, -2, "cpath"); - lua_pop( K, 1); lua_getglobal( K, "require"); lua_pushlstring( K, name, name_len); res = lua_pcall( K, 1, 0, 0); if( res != 0) { - char const *err = luaL_checkstring( K, -1); + char const* err = luaL_checkstring( K, -1); luaL_error( L, "error requiring '%s' in keeper state: %s", name, err); } - STACK_END(K, 0) + STACK_END( K, 0) MUTEX_UNLOCK( &GKeepers[i].lock_); } - lua_pop( L, 3); - STACK_END(L, 0) + STACK_END( L, 0) } -struct s_Keeper *keeper_acquire( const void *ptr) +struct s_Keeper* keeper_acquire( void const* ptr) { // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) if( GNbKeepers == 0) @@ -691,19 +680,19 @@ struct s_Keeper *keeper_acquire( const void *ptr) } } -void keeper_release( struct s_Keeper *K) +void keeper_release( struct s_Keeper* K) { //-- K->count; if( K) MUTEX_UNLOCK( &K->lock_); } -void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel) +void keeper_toggle_nil_sentinels( lua_State* L, int _val_i, int _nil_to_sentinel) { int i, n = lua_gettop( L); /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours) */ - void *nil_sentinel = &GNbKeepers; + void* nil_sentinel = &GNbKeepers; for( i = _val_i; i <= n; ++ i) { if( _nil_to_sentinel) diff --git a/src/keeper.h b/src/keeper.h index decae4a..15a5a41 100644 --- a/src/keeper.h +++ b/src/keeper.h @@ -13,7 +13,7 @@ struct s_Keeper // problem: maybe on some platforms (linux) atexit() is called after DLL/so are unloaded... #define HAVE_KEEPER_ATEXIT_DESINIT 0 -char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create); +char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create); #if !HAVE_KEEPER_ATEXIT_DESINIT void close_keepers( void); #endif // HAVE_KEEPER_ATEXIT_DESINIT diff --git a/src/lanes.c b/src/lanes.c index 8a67621..6fec951 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -52,13 +52,13 @@ * ... */ -char const* VERSION = "3.4.2"; +char const* VERSION = "3.4.3"; /* =============================================================================== Copyright (C) 2007-10 Asko Kauppi - 2011-12 Benoit Germain + 2011-13 Benoit Germain Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1114,7 +1114,8 @@ static int run_finalizers( lua_State*L, int lua_rc ) // // LUA_ERRRUN / LUA_ERRMEM - if (rc!=0) { + if( rc != LUA_OK) + { // [-1]: error message // // If one finalizer fails, don't run the others. Return this @@ -1269,7 +1270,7 @@ static bool_t selfdestruct_remove( struct s_lane *s ) // Initialized by 'init_once_LOCKED()': the deep userdata Linda object // used for timers (each lane will get a proxy to this) // -volatile DEEP_PRELUDE *timer_deep; // = NULL +volatile DEEP_PRELUDE* timer_deep; // = NULL /* * Process end; cancel any still free-running threads @@ -1349,7 +1350,7 @@ static int selfdestruct_gc( lua_State*L) t_now = now_secs(); if( n == 0 || ( t_now >= t_until)) { - DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now))); + DEBUGSPEW_CODE( fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now))); break; } } @@ -1379,7 +1380,7 @@ static int selfdestruct_gc( lua_State*L) //we want to free memory and such when we exit. // 2.0.2: at least timer lane is still here // - DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); + DEBUGSPEW_CODE( fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); n=0; #else // first thing we did was to raise the linda signals the threads were waiting on (if any) @@ -1409,7 +1410,7 @@ static int selfdestruct_gc( lua_State*L) } MUTEX_UNLOCK( &selfdestruct_cs ); - DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); + DEBUGSPEW_CODE( fprintf( stderr, "Killed %d lane(s) at process end.\n", n)); #endif } #if !HAVE_KEEPER_ATEXIT_DESINIT @@ -1654,6 +1655,39 @@ LUAG_FUNC( set_debug_threadname) return 0; } +#if USE_DEBUG_SPEW +// can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-( +// LUA_ERRERR doesn't have the same value +struct errcode_name +{ + int code; + char const* name; +}; + +static struct errcode_name s_errcodes[] = +{ + { LUA_OK, "LUA_OK"}, + { LUA_YIELD, "LUA_YIELD"}, + { LUA_ERRRUN, "LUA_ERRRUN"}, + { LUA_ERRSYNTAX, "LUA_ERRSYNTAX"}, + { LUA_ERRMEM, "LUA_ERRMEM"}, + { LUA_ERRGCMM, "LUA_ERRGCMM"}, + { LUA_ERRERR, "LUA_ERRERR"}, +}; +static char const* get_errcode_name( int _code) +{ + int i; + for( i = 0; i < 7; ++ i) + { + if( s_errcodes[i].code == _code) + { + return s_errcodes[i].name; + } + } + return ""; +} +#endif // USE_DEBUG_SPEW + //--- static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) { @@ -1713,7 +1747,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) // Lua 5.1 error handler is limited to one return value; taking stack trace // via registry // - if (rc!=0) { + if( rc != 0) + { STACK_GROW(L,1); lua_pushlightuserdata( L, STACK_TRACE_KEY ); lua_gettable(L, LUA_REGISTRYINDEX); @@ -1733,16 +1768,19 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) // [2..top]: parameters // rc= lua_pcall( L, lua_gettop(L)-1, LUA_MULTRET, 0 /*no error handler*/ ); - // 0: no error - // LUA_ERRRUN: a runtime error (error pushed on stack) - // LUA_ERRMEM: memory allocation error + // LUA_OK(0): no error + // LUA_ERRRUN(2): a runtime error (error pushed on stack) + // LUA_ERRMEM(4): memory allocation error #endif -//STACK_DUMP(L); + DEBUGSPEW_CODE( fprintf( stderr, "Lane %p body: %s\n", L, get_errcode_name( rc))); + //STACK_DUMP(L); // Call finalizers, if the script has set them up. // - rc2= run_finalizers(L,rc); - if (rc2!=0) { + rc2 = run_finalizers( L, rc); + DEBUGSPEW_CODE( fprintf( stderr, "Lane %p finalizer: %s\n", L, get_errcode_name( rc2))); + if( rc2 != LUA_OK) + { // Error within a finalizer! // // [-1]: error message @@ -1766,7 +1804,6 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) s->L = L = 0; lane_cleanup( s); - } else { @@ -1809,10 +1846,10 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) // helper function to require a module in the keeper states and in the target state // source state contains module name at the top of the stack -static void require_one_module( lua_State*L, lua_State*L2, bool_t _fatal) +static void require_one_module( lua_State* L, lua_State* L2, bool_t _fatal) { size_t len; - char const *name = lua_tolstring( L, -1, &len); + char const* name = lua_tolstring( L, -1, &len); // require the module in the target lane STACK_GROW( L2, 2); lua_getglobal( L2, "require"); @@ -1872,39 +1909,11 @@ LUAG_FUNC( thread_new ) ASSERT_L( lua_gettop(L2) == 0); - // package.path - STACK_CHECK(L) - STACK_CHECK(L2) + // package if( package) { - if( lua_type( L, package) != LUA_TTABLE) - { - return luaL_error( L, "expected package as table, got %s", luaL_typename( L, package)); - } - lua_getglobal( L2, "package"); - if( !lua_isnil( L2, -1)) // package library not loaded: do nothing - { - int i; - // package.loaders is renamed package.searchers in Lua 5.2 - char const* entries[] = { "path", "cpath", "preload", (LUA_VERSION_NUM == 501) ? "loaders" : "searchers", NULL}; - for( i = 0; entries[i]; ++ i) - { - lua_getfield( L, package, entries[i]); - if( lua_isnil( L, -1)) - { - lua_pop( L, 1); - } - else - { - luaG_inter_move( L, L2, 1); // moves the entry to L2 - lua_setfield( L2, -2, entries[i]); // set package[entries[i]] - } - } - } - lua_pop( L2, 1); + luaG_inter_copy_package( L, L2, package); } - STACK_END(L2,0) - STACK_END(L,0) // modules to require in the target lane *before* the function is transfered! @@ -1924,14 +1933,14 @@ LUAG_FUNC( thread_new ) { int nbRequired = 1; // should not happen, was checked in lanes.lua before calling thread_new() - if (lua_type(L, required) != LUA_TTABLE) + if( lua_type( L, required) != LUA_TTABLE) { return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); } lua_pushnil( L); while( lua_next( L, required) != 0) { - if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) + if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) { return luaL_error( L, "required module list should be a list of strings"); } @@ -1949,13 +1958,13 @@ LUAG_FUNC( thread_new ) // Appending the specified globals to the global environment // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... // - if (glob!=0) + if( glob != 0) { STACK_CHECK(L) STACK_CHECK(L2) if( !lua_istable( L, glob)) { - return luaL_error( L, "Expected table, got %s", luaL_typename(L,glob)); + return luaL_error( L, "Expected table, got %s", luaL_typename( L, glob)); } lua_pushnil( L); @@ -1987,7 +1996,7 @@ LUAG_FUNC( thread_new ) } STACK_MID(L,0) } - else if( lua_type(L, 1) == LUA_TSTRING) + else if( lua_type( L, 1) == LUA_TSTRING) { // compile the string if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) @@ -2089,7 +2098,7 @@ LUAG_FUNC( thread_gc) // Make sure a kill has proceeded, before cleaning up the data structure. // // NO lua_close() in this case because we don't know where execution of the state was interrupted - DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); + DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **")); // make sure the thread is no longer running, just like thread_join() if(! THREAD_ISNULL( s->thread)) THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); @@ -2100,7 +2109,7 @@ LUAG_FUNC( thread_gc) lua_close( s->L); s->L = 0; } - DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); + DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **")); } else if( s->status < DONE) { @@ -2265,7 +2274,7 @@ LUAG_FUNC( thread_join) break; default: - DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status)); + DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status)); ASSERT_L( FALSE ); ret= 0; } lua_close( L2); @@ -2547,9 +2556,9 @@ static const struct luaL_Reg lanes_functions [] = { /* * One-time initializations */ -static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout, bool_t _track_lanes) +static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout, bool_t _track_lanes) { - const char *err; + char const* err; #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) now_secs(); // initialize 'now_secs()' internal offset @@ -2604,7 +2613,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r } #endif #endif - err = init_keepers( nbKeepers, _on_state_create); + err = init_keepers( L, nbKeepers, _on_state_create); if (err) { (void) luaL_error( L, "Unable to initialize: %s", err ); @@ -2612,7 +2621,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r // Initialize 'timer_deep'; a common Linda object shared by all states // - ASSERT_L( timer_deep_ref && (!(*timer_deep_ref)) ); + ASSERT_L( timer_deep == NULL); STACK_CHECK(L) { @@ -2627,8 +2636,8 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer // - *timer_deep_ref= * (DEEP_PRELUDE**) lua_touserdata( L, -1 ); - ASSERT_L( (*timer_deep_ref) && (*timer_deep_ref)->refcount==1 && (*timer_deep_ref)->deep ); + timer_deep = * (DEEP_PRELUDE**) lua_touserdata( L, -1); + ASSERT_L( timer_deep && (timer_deep->refcount == 1) && timer_deep->deep); // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. // So store a reference that we will never actually use. @@ -2644,8 +2653,8 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r lua_setfield( L, -2, "__metatable"); lua_setmetatable( L, -2); } - lua_insert(L, -2); // Swap key with the Linda object - lua_rawset(L, LUA_REGISTRYINDEX); + lua_insert( L, -2); // Swap key with the Linda object + lua_rawset( L, LUA_REGISTRYINDEX); } STACK_END(L,0) @@ -2653,107 +2662,147 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r static volatile long s_initCount = 0; -LUAG_FUNC( configure ) +// upvalue 1: module name +// upvalue 2: module table +LUAG_FUNC( configure) { - char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); - // all parameter checks are done lua-side - int const nbKeepers = (int)lua_tointeger( L, 1); - lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; - lua_Number shutdown_timeout = lua_tonumber( L, 3); - bool_t track_lanes = lua_toboolean( L, 4); - /* - * Making one-time initializations. - * - * When the host application is single-threaded (and all threading happens via Lanes) - * there is no problem. But if the host is multithreaded, we need to lock around the - * initializations. - */ + char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); + // all parameter checks are done lua-side + int const nbKeepers = (int)lua_tointeger( L, 1); + lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; + lua_Number shutdown_timeout = lua_tonumber( L, 3); + bool_t track_lanes = lua_toboolean( L, 4); + + STACK_CHECK( L) + // Create main module interface table + lua_pushvalue( L, lua_upvalueindex( 2)); // ... M + // remove configure() (this function) from the module interface + lua_pushnil( L); // ... M nil + lua_setfield( L, -2, "configure"); // ... M + // add functions to the module's table + luaG_registerlibfuncs( L, lanes_functions); + STACK_MID( L, 1) + + // metatable for threads + // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join } + // + lua_newtable( L); // ... M mt + lua_pushcfunction( L, LG_thread_gc); // ... M mt LG_thread_gc + lua_setfield( L, -2, "__gc"); // ... M mt + lua_pushcfunction( L, LG_thread_index); // ... M mt LG_thread_index + lua_setfield( L, -2, "__index"); // ... M mt + lua_getglobal( L, "error"); // ... M mt error + ASSERT_L( lua_isfunction( L, -1)); + lua_setfield( L, -2, "cached_error"); // ... M mt + lua_getglobal( L, "tostring"); // ... M mt tostring + ASSERT_L( lua_isfunction( L, -1)); + lua_setfield( L, -2, "cached_tostring"); // ... M mt + lua_pushcfunction( L, LG_thread_join); // ... M mt LG_thread_join + lua_setfield( L, -2, "join"); // ... M mt + lua_pushcfunction( L, LG_thread_cancel); // ... M mt LG_thread_cancel + lua_setfield( L, -2, "cancel"); // ... M mt + lua_pushliteral( L, "Lane"); // ... M mt "Lane" + lua_setfield( L, -2, "__metatable"); // ... M mt + + lua_pushcclosure( L, LG_thread_new, 1); // ... M LG_thread_new + lua_setfield(L, -2, "thread_new"); // ... M + + lua_pushstring(L, VERSION); // ... M VERSION + lua_setfield(L, -2, "version"); // ... M + + lua_pushinteger(L, THREAD_PRIO_MAX); // ... M THREAD_PRIO_MAX + lua_setfield(L, -2, "max_prio"); // ... M + + lua_pushlightuserdata( L, CANCEL_ERROR); // ... M CANCEL_ERROR + lua_setfield(L, -2, "cancel_error"); // ... 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) + // for example in package.loaded.lanes.core.* + populate_func_lookup_table( L, -1, name); + + // record all existing C/JIT-fast functions + // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack + lua_pushglobaltable( L); // ... M _G + populate_func_lookup_table( L, -1, NULL); + lua_pop( L, 1); // ... M + + STACK_MID( L, 1) + /* + * Making one-time initializations. + * + * When the host application is single-threaded (and all threading happens via Lanes) + * there is no problem. But if the host is multithreaded, we need to lock around the + * initializations. + * + * we must do this after the populate_func_lookup_table is called, else populating the keepers will fail + * because this makes a copy of packages.loaders, which requires the lookup tables to exist! + */ #if THREADAPI == THREADAPI_WINDOWS - { - static volatile int /*bool*/ go_ahead; // = 0 - if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) - { - init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout, track_lanes); - go_ahead= 1; // let others pass - } - else - { - while( !go_ahead ) { Sleep(1); } // changes threads - } - } + { + static volatile int /*bool*/ go_ahead; // = 0 + if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) + { + init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes); + go_ahead = 1; // let others pass + } + else + { + while( !go_ahead ) { Sleep(1); } // changes threads + } + } #else // THREADAPI == THREADAPI_PTHREAD - if( s_initCount == 0) - { - static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock( &my_lock); - { - // Recheck now that we're within the lock - // - if( s_initCount == 0) - { - init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout, track_lanes); - s_initCount = 1; - } - } - pthread_mutex_unlock(&my_lock); - } + if( s_initCount == 0) + { + static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock( &my_lock); + { + // Recheck now that we're within the lock + // + if( s_initCount == 0) + { + init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes); + s_initCount = 1; + } + } + pthread_mutex_unlock( &my_lock); + } #endif // THREADAPI == THREADAPI_PTHREAD - assert( timer_deep != 0 ); - - // Create main module interface table - lua_pushvalue( L, lua_upvalueindex( 2)); - // remove configure() (this function) from the module interface - lua_pushnil( L); - lua_setfield( L, -2, "configure"); - // add functions to the module's table - luaG_registerlibfuncs(L, lanes_functions); - - // metatable for threads - // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join } - // - lua_newtable( L); - lua_pushcfunction( L, LG_thread_gc); - lua_setfield( L, -2, "__gc"); - lua_pushcfunction( L, LG_thread_index); - lua_setfield( L, -2, "__index"); - lua_getglobal( L, "error"); - ASSERT_L( lua_isfunction( L, -1)); - lua_setfield( L, -2, "cached_error"); - lua_getglobal( L, "tostring"); - ASSERT_L( lua_isfunction( L, -1)); - lua_setfield( L, -2, "cached_tostring"); - lua_pushcfunction( L, LG_thread_join); - lua_setfield( L, -2, "join"); - lua_pushcfunction( L, LG_thread_cancel); - lua_setfield( L, -2, "cancel"); - lua_pushliteral( L, "Lane"); - lua_setfield( L, -2, "__metatable"); - - lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param - lua_setfield(L, -2, "thread_new"); - - luaG_push_proxy( L, linda_id, (DEEP_PRELUDE *) timer_deep ); - lua_setfield(L, -2, "timer_gateway"); - - lua_pushstring(L, VERSION); - lua_setfield(L, -2, "version"); - - lua_pushinteger(L, THREAD_PRIO_MAX); - lua_setfield(L, -2, "max_prio"); - - lua_pushlightuserdata( L, CANCEL_ERROR ); - lua_setfield(L, -2, "cancel_error"); - - // 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) - populate_func_lookup_table( L, -1, name); - lua_pop( L, 1); - // record all existing C/JIT-fast functions - 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); - lua_pop( L, 1); // done with globals table, pop it - // Return nothing - return 0; + assert( timer_deep != NULL); + STACK_MID( L, 1) + + // init_once_LOCKED initializes timer_deep, so we must do this after, of course + luaG_push_proxy( L, linda_id, (DEEP_PRELUDE*) timer_deep); // ... M timer_deep + lua_setfield( L, -2, "timer_gateway"); // ... M + + lua_pop( L, 1); // ... + STACK_END( L, 0) + // Return nothing + return 0; +} + +// helper to have correct callstacks when crashing a Win32 running on 64 bits Windows +// don't forget to toggle Debug/Exceptions/Win32 in visual Studio too! +void EnableCrashingOnCrashes() +{ +#if 0 && defined PLATFORM_WIN32 + typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags); + typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags); + const DWORD EXCEPTION_SWALLOWING = 0x1; + + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); + tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); + if (pGetPolicy && pSetPolicy) + { + DWORD dwFlags; + if (pGetPolicy(&dwFlags)) + { + // Turn off the filter + pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); + } + } +#endif // PLATFORM_WIN32 } int @@ -2762,6 +2811,8 @@ __declspec(dllexport) #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) luaopen_lanes_core( lua_State* L) { + EnableCrashingOnCrashes(); + STACK_GROW( L, 3); STACK_CHECK( L) diff --git a/src/lanes.lua b/src/lanes.lua index 8c135c2..6bd9e44 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -210,10 +210,10 @@ local valid_libs= { ["string"]= true, ["math"]= true, ["debug"]= true, + ["bit32"]= true, -- Lua 5.2 only, ignored silently under 5.1 -- ["base"]= true, - ["coroutine"]= true, - ["*"]= true + ["coroutine"]= true } -- PUBLIC LANES API @@ -251,11 +251,19 @@ local function gen( ... ) -- Check 'libs' already here, so the error goes in the right place -- (otherwise will be noticed only once the generator is called) + -- "*" is a special case that doesn't require individual checking -- - if libs then - for s in string_gmatch(libs, "[%a*]+") do + if libs and libs ~= "*" then + local found = {} + -- check that the caller only provides reserved library names + for s in string_gmatch(libs, "[%a%d]+") do if not valid_libs[s] then - error( "Bad library name: "..s ) + error( "Bad library name: " .. s) + else + found[s] = (found[s] or 0) + 1 + if found[s] > 1 then + error( "libs specification contains '" .. s .. "' more than once") + end end end end diff --git a/src/tools.c b/src/tools.c index 2629fd3..fe1728d 100644 --- a/src/tools.c +++ b/src/tools.c @@ -42,6 +42,8 @@ THE SOFTWARE. #include #include +DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); + MUTEX_T deep_lock; MUTEX_T mtid_lock; @@ -94,45 +96,52 @@ void luaG_dump( lua_State* L ) { /*---=== luaG_newstate ===---*/ -static const luaL_Reg libs[] = { - { LUA_LOADLIBNAME, luaopen_package }, - { LUA_TABLIBNAME, luaopen_table }, - { LUA_IOLIBNAME, luaopen_io }, - { LUA_OSLIBNAME, luaopen_os }, - { LUA_STRLIBNAME, luaopen_string }, - { LUA_MATHLIBNAME, luaopen_math }, - { LUA_DBLIBNAME, luaopen_debug }, - // - { "base", NULL }, // ignore "base" (already acquired it) - { "coroutine", NULL }, // part of Lua 5.1 base package - { NULL, NULL } +static const luaL_Reg libs[] = +{ + { LUA_LOADLIBNAME, luaopen_package}, + { LUA_TABLIBNAME, luaopen_table}, + { LUA_IOLIBNAME, luaopen_io}, + { LUA_OSLIBNAME, luaopen_os}, + { LUA_STRLIBNAME, luaopen_string}, + { LUA_MATHLIBNAME, luaopen_math}, +#if LUA_VERSION_NUM >= 502 + { LUA_BITLIBNAME, luaopen_bit32}, +#endif // LUA_VERSION_NUM + { LUA_DBLIBNAME, luaopen_debug}, + // + { "base", NULL }, // ignore "base" (already acquired it) + { LUA_COLIBNAME, NULL }, // part of Lua 5.[1|2] base package + { NULL, NULL } }; -static bool_t openlib( lua_State *L, const char *name, size_t len ) { - - unsigned i; - bool_t all= strncmp( name, "*", len ) == 0; - - for( i=0; libs[i].name; i++ ) +static void open1lib( lua_State* L, char const* name, size_t len) +{ + int i; + for( i = 0; libs[i].name; ++ i) { - if (all || (strncmp(name, libs[i].name, len) ==0)) + if( strncmp( name, libs[i].name, len) == 0) { - if (libs[i].func) + if( libs[i].func) { - STACK_GROW(L,1); - STACK_CHECK(L) + DEBUGSPEW_CODE( fprintf( stderr, "opening %.*s library\n", len, name)); + STACK_GROW( L, 1); + STACK_CHECK( L) lua_pushcfunction( L, libs[i].func); // pushes the module table on the stack lua_call( L, 0, 1); populate_func_lookup_table( L, -1, libs[i].name); - // remove the module when we are done +#if LUA_VERSION_NUM >= 502 + // Lua 5.2: luaopen_x doesn't create the global, we have to do it ourselves! + lua_setglobal( L, libs[i].name); +#else // LUA_VERSION_NUM + // Lua 5.1: remove the module when we are done lua_pop( L, 1); - STACK_END(L, 0) +#endif // LUA_VERSION_NUM + STACK_END( L, 0) } - if (!all) return TRUE; + break; } } - return all; } static int dummy_writer(lua_State *L, const void* p, size_t sz, void* ud) @@ -323,6 +332,7 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _ // remove table name from fqn stack lua_pushnil( L); // ... {_i} {bfc} k nil lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k + DEBUGSPEW_CODE( fprintf( stderr, "%.*spopulating: %s\n", _i, debugspew_indent, newName)); -- _depth; } else @@ -372,12 +382,12 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _ /* * create a "fully.qualified.name" <-> function equivalence database */ -void populate_func_lookup_table( lua_State *L, int _i, char const *_name) +void populate_func_lookup_table( lua_State* L, int _i, char const* _name) { int const ctx_base = lua_gettop( L) + 1; int const in_base = lua_absindex( L, _i); int const start_depth = _name ? 1 : 0; - //printf( "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL"); + DEBUGSPEW_CODE( fprintf( stderr, "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL")); STACK_GROW( L, 3); STACK_CHECK( L) lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}? @@ -423,8 +433,6 @@ void populate_func_lookup_table( lua_State *L, int _i, char const *_name) lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create) { - char const* p; - unsigned int len; lua_State* const L = luaL_newstate(); // no libs, or special init func (not even 'base') @@ -447,31 +455,43 @@ lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create) { if( libs[0] == '*' && libs[1] == 0) // special "*" case (mainly to help with LuaJIT compatibility) { + DEBUGSPEW_CODE( fprintf( stderr, "opening ALL base libraries\n")); luaL_openlibs( L); libs = NULL; // done with libs } else { + DEBUGSPEW_CODE( fprintf( stderr, "opening base library\n")); lua_pushcfunction( L, luaopen_base); lua_call( L, 0, 0); } } + // after opening base, register the functions it exported 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); lua_pop( L, 1); + STACK_MID( L, 0); - if( libs) { - for( p = libs; *p; p += len) + char const* p; + unsigned int len = 0; + if( libs) { - len=0; - while (*p && !is_name_char(*p)) p++; // bypass delimiters - while (is_name_char(p[len])) len++; // bypass name - if (len && (!openlib( L, p, len ))) - break; + for( p = libs; *p; p += len) + { + len = 0; + // skip delimiters + while( *p && !is_name_char( *p)) + ++ p; + // skip name + while( is_name_char( p[len])) + ++ len; + // open library + open1lib( L, p, len); + } + serialize_require( L); } - serialize_require( L); } STACK_END(L,0) lua_gc( L, LUA_GCRESTART, 0); @@ -1126,9 +1146,9 @@ static bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, * * Always pushes a function to 'L2'. */ -static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ); +static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i); -static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) +static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) { void * const aspointer = (void*)lua_topointer( L, i); // TBD: Merge this and same code for tables @@ -1228,6 +1248,27 @@ int discover_object_name_recur( lua_State* L, int _shortest, int _length) else if( lua_istable( L, -1)) { _shortest = discover_object_name_recur( L, _shortest, _length); + // search in the table's metatable too + if( lua_getmetatable( L, -1)) + { + if( lua_istable( L, -1)) + { + _shortest = discover_object_name_recur( L, _shortest, _length); + } + lua_pop( L, 1); + } + } + else if( lua_isuserdata( L, -1)) + { + // search in the object's metatable (some modules are built that way) + if( lua_getmetatable( L, -1)) + { + if( lua_istable( L, -1)) + { + _shortest = discover_object_name_recur( L, _shortest, _length); + } + lua_pop( L, 1); + } } // make ready for next iteration lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k @@ -1298,9 +1339,10 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i) if( !fqn) { char const* from; - lua_pushvalue( L, i); // ... f ... f // try to discover the name of the function we want to send - luaG_nameof( L); // ... f ... "type" "name" + lua_pushcfunction( L, luaG_nameof); // ... f ...luaG_nameof + lua_pushvalue( L, i); // ... f ... luaG_nameof f + lua_call( L, 1, 2); // ... f ... "type" "name" lua_getglobal( L, "decoda_name"); // ... f ... "type" "name" decoda_name from = lua_tostring( L, -1); (void) luaL_error( L, "%s '%s' not found in %s origin transfer database.", lua_tostring( L, -3), lua_tostring( L, -2), from ? from : "main"); @@ -1327,15 +1369,6 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i) STACK_END( L2, 1) } -#define LOG_FUNC_INFO 0 -#if LOG_FUNC_INFO -#define LOG_FUNC_INFO_CODE(_code) _code -#else // LOG_FUNC_INFO -#define LOG_FUNC_INFO_CODE(_code) -#endif // LOG_FUNC_INFO - -LOG_FUNC_INFO_CODE( static char const* s_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); - /* * Copy a function over, which has not been found in the cache. * L2 has the cache key for this function at the top of the stack @@ -1350,7 +1383,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin FuncSubType funcSubType; lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions - ASSERT_L( L2_cache_i != 0); // ... {cache} ... p + ASSERT_L( L2_cache_i != 0); // ... {cache} ... p STACK_GROW(L,2); STACK_CHECK(L) @@ -1362,7 +1395,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin // if already on top of the stack, no need to push again int needToPush = (i != (uint_t)lua_gettop( L)); if( needToPush) - lua_pushvalue( L, i); + lua_pushvalue( L, i); // ... f luaL_buffinit( L, &b); // @@ -1374,12 +1407,13 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin luaL_error( L, "internal error: function dump failed."); } - luaL_pushresult( &b); // pushes dumped string on 'L' + // pushes dumped string on 'L' + luaL_pushresult( &b); // ... f b // if not pushed, no need to pop if( needToPush) { - lua_remove( L, -2); + lua_remove( L, -2); // ... b } // transfer the bytecode, then the upvalues, to create a similar closure @@ -1392,15 +1426,16 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin // { lua_Debug ar; - lua_pushvalue( L, i); - lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function + lua_pushvalue( L, i); // ... b f + // fills 'name' 'namewhat' and 'linedefined', pops function + lua_getinfo(L, ">nS", &ar); // ... b name = ar.namewhat; fprintf( stderr, "%.*sFNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL } #endif // LOG_FUNC_INFO { size_t sz; - char const* s = lua_tolstring( L, -1, &sz); + char const* s = lua_tolstring( L, -1, &sz); // ... b ASSERT_L( s && sz); STACK_GROW( L2, 2); // Note: Line numbers seem to be taken precisely from the @@ -1409,7 +1444,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin // // TBD: Can we get the function's original name through, as well? // - if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function + if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function { // chunk is precompiled so only LUA_ERRMEM can happen // "Otherwise, it pushes an error message" @@ -1417,38 +1452,50 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin STACK_GROW( L, 1); luaL_error( L, "%s", lua_tostring( L2, -1)); } - lua_pop( L, 1); // remove the dumped string + // remove the dumped string + lua_pop( L, 1); // ... // now set the cache as soon as we can. // this is necessary if one of the function's upvalues references it indirectly // we need to find it in the cache even if it isn't fully transfered yet - lua_insert( L2, -2); // ... {cache} ... function p - lua_pushvalue( L2, -2); // ... {cache} ... function p function + lua_insert( L2, -2); // ... {cache} ... function p + lua_pushvalue( L2, -2); // ... {cache} ... function p function // cache[p] = function - lua_rawset( L2, L2_cache_i); // ... {cache} ... function + lua_rawset( L2, L2_cache_i); // ... {cache} ... function } STACK_MID( L, 0) /* push over any upvalues; references to this function will come from * cache so we don't end up in eternal loop. + * Lua5.2: one of the upvalues is _ENV, which we don't want to copy! + * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! */ { - LOG_FUNC_INFO_CODE( char const* upname); - for( n = 0; (LOG_FUNC_INFO_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) - { - LOG_FUNC_INFO_CODE( fprintf( stderr, "%.*sUPNAME[%d]: %s\n", i, s_indent, n, upname)); - // v 3.4.2: we now longer need to handle this special case, because the general mechanism can take care of it just fine - /*if( (!cfunc) && lua_equal( L, i, -1)) + DEBUGSPEW_CODE( char const* upname); +#if LUA_VERSION_NUM == 502 + // With Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) + // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... + // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table + lua_pushglobaltable( L); // ... _G +#endif // LUA_VERSION_NUM + for( n = 0; (DEBUGSPEW_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) + { // ... _G up[n] + DEBUGSPEW_CODE( fprintf( stderr, "%.*sUPNAME[%d]: %s\n", i, debugspew_indent, n, upname)); +#if LUA_VERSION_NUM == 502 + if( lua_rawequal( L, -1, -2)) // is the upvalue equal to the global table? { - // Lua closure that has a (recursive) upvalue to itself - lua_pushvalue( L2, -n - 1); // ... {cache} ... function upvalues... + lua_pushglobaltable( L2); // ... {cache} ... function } - else*/ + else +#endif // LUA_VERSION_NUM { - if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL)) + if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL)) // ... {cache} ... function luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); } - lua_pop( L, 1); + lua_pop( L, 1); // ... _G } +#if LUA_VERSION_NUM == 502 + lua_pop( L, 1); // ... +#endif // LUA_VERSION_NUM } // L2: function + 'n' upvalues (>=0) @@ -1459,7 +1506,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin int func_index = lua_gettop( L2) - n; for( ; n > 0; -- n) { - char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function + char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function // // "assigns the value at the top of the stack to the upvalue and returns its name. // It also pops the value from the stack." @@ -1467,16 +1514,16 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin ASSERT_L( rc); // not having enough slots? } // once all upvalues have been set we are left - // with the function at the top of the stack // ... {cache} ... function + // with the function at the top of the stack // ... {cache} ... function } } } else // C function OR LuaJIT fast function!!! { - lua_pop( L2, 1); // ... {cache} ... - LOG_FUNC_INFO_CODE( fprintf( stderr, "%.*sFNAME: [C] function %p \n", i, s_indent, cfunc)); + lua_pop( L2, 1); // ... {cache} ... + DEBUGSPEW_CODE( fprintf( stderr, "%.*sFNAME: [C] function %p \n", i, debugspew_indent, cfunc)); // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up - lookup_native_func( L2, L, i); // ... {cache} ... function + lookup_native_func( L2, L, i); // ... {cache} ... function } STACK_END( L, 0) } @@ -1518,8 +1565,8 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u break; case LUA_TSTRING: { - size_t len; const char *s = lua_tolstring( L, i, &len ); - LOG_FUNC_INFO_CODE( if( vt == VT_KEY) fprintf( stderr, "%.*sKEY: %s\n", i, s_indent, s)); + size_t len; const char* s = lua_tolstring( L, i, &len); + DEBUGSPEW_CODE( if( vt == VT_KEY) fprintf( stderr, "%.*sKEY: %s\n", i, debugspew_indent, s)); lua_pushlstring( L2, s, len ); } break; @@ -1735,7 +1782,7 @@ int luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n) } /* - * Remove the cache table. Persistant caching would cause i.e. multiple + * Remove the cache table. Persistent caching would cause i.e. multiple * messages passed in the same table to use the same table also in receiving * end. */ @@ -1755,13 +1802,49 @@ int luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n) } -int luaG_inter_move( lua_State* L, lua_State *L2, uint_t n ) +int luaG_inter_move( lua_State* L, lua_State* L2, uint_t n) { int ret = luaG_inter_copy( L, L2, n); lua_pop( L, (int) n); return ret; } +void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx) +{ + // package + STACK_CHECK( L) + STACK_CHECK( L2) + _idx = lua_absindex( L, _idx); + if( lua_type( L, _idx) != LUA_TTABLE) + { + (void) luaL_error( L, "expected package as table, got %s", luaL_typename( L, _idx)); + } + lua_getglobal( L2, "package"); + if( !lua_isnil( L2, -1)) // package library not loaded: do nothing + { + int i; + // package.loaders is renamed package.searchers in Lua 5.2 + char const* entries[] = { "path", "cpath", "preload", (LUA_VERSION_NUM == 501) ? "loaders" : "searchers", NULL}; + for( i = 0; entries[i]; ++ i) + { + lua_getfield( L, _idx, entries[i]); + if( lua_isnil( L, -1)) + { + lua_pop( L, 1); + } + else + { + luaG_inter_move( L, L2, 1); // moves the entry to L2 + lua_setfield( L2, -2, entries[i]); // set package[entries[i]] + } + } + } + lua_pop( L2, 1); + STACK_END( L2, 0) + STACK_END( L, 0) +} + + /*---=== Serialize require ===--- */ diff --git a/src/tools.h b/src/tools.h index 4a77a6a..d0169cf 100644 --- a/src/tools.h +++ b/src/tools.h @@ -25,6 +25,8 @@ #define lua_getuservalue lua_getfenv #define lua_rawlen lua_objlen #define luaG_registerlibfuncs( L, _funcs) luaL_register( L, NULL, _funcs) +#define LUA_OK 0 +#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1, we don't care about the actual value #endif // LUA_VERSION_NUM == 501 // wrap Lua 5.2 calls under Lua 5.1 API when it is simpler that way @@ -33,14 +35,21 @@ #define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) #endif // LUA_VERSION_NUM == 502 +#define USE_DEBUG_SPEW 0 +#if USE_DEBUG_SPEW +extern char const* debugspew_indent; +#define DEBUGSPEW_CODE(_code) _code +#else // USE_DEBUG_SPEW +#define DEBUGSPEW_CODE(_code) +#endif // USE_DEBUG_SPEW + + #ifdef NDEBUG #define _ASSERT_L(lua,c) /*nothing*/ #define STACK_CHECK(L) /*nothing*/ #define STACK_MID(L,c) /*nothing*/ #define STACK_END(L,c) /*nothing*/ #define STACK_DUMP(L) /*nothing*/ - #define DEBUG() /*nothing*/ - #define DEBUGEXEC(_code) {} /*nothing*/ #else #define _ASSERT_L(lua,c) do { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } while( 0) // @@ -50,8 +59,6 @@ #define STACK_END(L,change) STACK_MID(L,change) } #define STACK_DUMP(L) luaG_dump(L); - #define DEBUG() fprintf( stderr, "<<%s %d>>\n", __FILE__, __LINE__ ); - #define DEBUGEXEC(_code) {_code;} /*nothing*/ #endif #define ASSERT_L(c) _ASSERT_L(L,c) @@ -73,7 +80,8 @@ typedef struct { void *deep; } DEEP_PRELUDE; -void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_userdata ); +void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_userdata); +void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx); int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n); int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); @@ -85,7 +93,7 @@ int luaG_nameof( lua_State* L); extern MUTEX_T deep_lock; extern MUTEX_T mtid_lock; -void populate_func_lookup_table( lua_State *L, int _i, char const *_name); +void populate_func_lookup_table( lua_State* L, int _i, char const* _name); void serialize_require( lua_State *L); extern MUTEX_T require_cs; -- cgit v1.2.3-55-g6feb