From 44540b9335f3bbd2f6fda3e13329b28ec76b6d7a Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 11 Oct 2013 20:08:32 +0200 Subject: version 3.7.0 * fix lanes.threads() not being available in a lane where lanes.configure() settings didn't contain track_lanes although the initial configure() call did. * require "lanes".configure() sequence is only necessary at the first require "lanes". * fix a crash at application shutdown where in some situations we could deinitialize the protected allocator mutex while a lane was still using it. * fix timers broken by change 69 --- src/keeper.c | 46 +++++++---- src/keeper.h | 2 +- src/lanes.c | 251 ++++++++++++++++++++++++++++++++------------------------ src/lanes.lua | 72 ++++++++-------- src/threading.c | 5 +- src/threading.h | 2 +- src/tools.c | 96 +++++++++++++++------- src/tools.h | 12 ++- 8 files changed, 288 insertions(+), 198 deletions(-) (limited to 'src') diff --git a/src/keeper.c b/src/keeper.c index bd232cd..c9b2039 100644 --- a/src/keeper.c +++ b/src/keeper.c @@ -70,7 +70,7 @@ typedef struct static keeper_fifo* prepare_fifo_access( lua_State* L, int idx) { keeper_fifo* fifo = (keeper_fifo*) lua_touserdata( L, idx); - if( fifo) + if( fifo != NULL) { idx = lua_absindex( L, idx); STACK_GROW( L, 1); @@ -286,15 +286,15 @@ int keepercall_receive( lua_State* L) { int top = lua_gettop( L); int i; - keeper_fifo* fifo; push_table( L, 1); // ud keys fifos lua_replace( L, 1); // fifos keys for( i = 2; i <= top; ++ i) { + keeper_fifo* fifo; lua_pushvalue( L, i); // fifos keys key[i] lua_rawget( L, 1); // fifos keys fifo fifo = prepare_fifo_access( L, -1); // fifos keys fifo - if( fifo && fifo->count > 0) + if( fifo != NULL && fifo->count > 0) { fifo_pop( L, fifo, 1); // fifos keys val if( !lua_isnil( L, -1)) @@ -332,7 +332,7 @@ int keepercall_receive_batched( lua_State* L) lua_rawget( L, 2); // key fifos fifo lua_remove( L, 2); // key fifo fifo = prepare_fifo_access( L, 2); // key fifo - if( fifo && fifo->count >= min_count) + if( fifo != NULL && fifo->count >= min_count) { fifo_pop( L, fifo, __min( max_count, fifo->count)); // key ... } @@ -360,7 +360,7 @@ int keepercall_limit( lua_State* L) lua_pushvalue( L, -1); // fifos key key lua_rawget( L, -3); // fifos key fifo fifo = (keeper_fifo*) lua_touserdata( L, -1); - if( !fifo) + if( fifo == NULL) { lua_pop( L, 1); // fifos key fifo_new( L); // fifos key fifo @@ -437,7 +437,7 @@ int keepercall_get( lua_State* L) lua_replace( L, 1); // fifos key lua_rawget( L, 1); // fifos fifo fifo = prepare_fifo_access( L, -1); // fifos fifo - if( fifo && fifo->count > 0) + if( fifo != NULL && fifo->count > 0) { lua_remove( L, 1); // fifo // read one value off the fifo @@ -497,7 +497,7 @@ int keepercall_count( lua_State* L) lua_rawget( L, 2); // out fifos keys fifo fifo = prepare_fifo_access( L, -1); // out fifos keys fifo lua_pop( L, 1); // out fifos keys - if( fifo) + if( fifo != NULL) { lua_pushinteger( L, fifo->count); // out fifos keys count lua_rawset( L, 1); // out fifos keys @@ -540,14 +540,17 @@ void close_keepers( void) for( i = 0; i < GNbKeepers; ++ i) { lua_State* L = GKeepers[i].L; - GKeepers[i].L = 0; + GKeepers[i].L = NULL; lua_close( L); } for( i = 0; i < GNbKeepers; ++ i) { MUTEX_FREE( &GKeepers[i].lock_); } - if( GKeepers) free( GKeepers); + if( GKeepers != NULL) + { + free( GKeepers); + } GKeepers = NULL; GNbKeepers = 0; } @@ -560,20 +563,29 @@ void close_keepers( void) * Note: Any problems would be design flaws; the created Lua state is left * unclosed, because it does not really matter. In production code, this * function never fails. +* settings table is at position 1 on the stack */ -char const* init_keepers( lua_State* L, int _on_state_create, int const _nbKeepers) +char const* init_keepers( lua_State* L) { - int i; - assert( _nbKeepers >= 1); - GNbKeepers = _nbKeepers; - GKeepers = malloc( _nbKeepers * sizeof( struct s_Keeper)); - for( i = 0; i < _nbKeepers; ++ i) + int i, on_state_create; + STACK_CHECK( L); + lua_getfield( L, 1, "nb_keepers"); + GNbKeepers = lua_tointeger( L, -1); + lua_pop( L, 1); + STACK_MID( L, 0); + assert( GNbKeepers >= 1); + + lua_getfield( L, 1, "on_state_create"); + on_state_create = lua_isnil( L, -1) ? -1 : lua_absindex( L, -1); + + GKeepers = malloc( GNbKeepers * sizeof( struct s_Keeper)); + for( i = 0; i < GNbKeepers; ++ i) { lua_State* K; DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "### init_keepers %d BEGIN\n" INDENT_END, i)); DEBUGSPEW_CODE( ++ debugspew_indent_depth); // we don't need any libs in the keeper states - K = luaG_newstate( L, _on_state_create, NULL); + K = luaG_newstate( L, on_state_create, NULL); STACK_CHECK( K); @@ -596,6 +608,8 @@ char const* init_keepers( lua_State* L, int _on_state_create, int const _nbKeepe GKeepers[i].L = K; //GKeepers[i].count = 0; } + lua_pop( L, 1); + STACK_END( L, 0); #if HAVE_KEEPER_ATEXIT_DESINIT atexit( atexit_close_keepers); #endif // HAVE_KEEPER_ATEXIT_DESINIT diff --git a/src/keeper.h b/src/keeper.h index 420eca1..2ef443d 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( lua_State* L, int _on_state_create, int const _nbKeepers); +char const* init_keepers( lua_State* L); #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 5ccc05a..3a3cdf0 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -52,7 +52,7 @@ * ... */ -char const* VERSION = "3.6.6"; +char const* VERSION = "3.7.0"; /* =============================================================================== @@ -1932,7 +1932,7 @@ LUAG_FUNC( thread_new) uint_t required = lua_isnoneornil( L, 8) ? 0 : 8; #define FIXED_ARGS 8 - uint_t args= lua_gettop(L) - FIXED_ARGS; + uint_t args = lua_gettop(L) - FIXED_ARGS; if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) { @@ -2002,8 +2002,19 @@ LUAG_FUNC( thread_new) } else { + // if is it "lanes" or "lanes.core", make sure we have copied the initial settings over + // 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); + } lua_pushlstring( L2, name, len); // require() name - lua_pcall( L2, 1, 1, 0); // ret + if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode + { + // propagate error to main state if any + luaG_inter_move( L2, L, 1, eLM_LaneBody); // + return lua_error( L); + } STACK_MID( L2, 1); // after requiring the module, register the functions it exported in our name<->function database populate_func_lookup_table( L2, -1, name); @@ -2110,7 +2121,7 @@ LUAG_FUNC( thread_new) ASSERT_L( s); //memset( s, 0, sizeof(struct s_lane) ); - s->L= L2; + s->L = L2; s->status= PENDING; s->waiting_on = NULL; s->debug_name = NULL; @@ -2128,8 +2139,8 @@ LUAG_FUNC( thread_new) // Set metatable for the userdata // - lua_pushvalue( L, lua_upvalueindex(1) ); - lua_setmetatable( L, -2 ); + lua_pushvalue( L, lua_upvalueindex( 1)); + lua_setmetatable( L, -2); STACK_MID( L, 1); // Clear environment for the userdata @@ -2140,11 +2151,11 @@ LUAG_FUNC( thread_new) // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still // do cancel tests at pending send/receive). // - lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); - lua_pushlightuserdata( L2, s ); - lua_rawset( L2, LUA_REGISTRYINDEX ); + lua_pushlightuserdata( L2, CANCEL_TEST_KEY); + lua_pushlightuserdata( L2, s); + lua_rawset( L2, LUA_REGISTRYINDEX); - if (cs) + if( cs) { lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs ); } @@ -2632,9 +2643,6 @@ LUAG_FUNC( wakeup_conv ) static const struct luaL_Reg lanes_functions [] = { {"linda", LG_linda}, {"now_secs", LG_now_secs}, -#if HAVE_LANE_TRACKING - {"threads", LG_threads}, -#endif // HAVE_LANE_TRACKING {"wakeup_conv", LG_wakeup_conv}, {"nameof", luaG_nameof}, {"set_singlethreaded", LG_set_singlethreaded}, @@ -2644,10 +2652,18 @@ static const struct luaL_Reg lanes_functions [] = { /* ** One-time initializations + * settings table it at position 1 on the stack */ -static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes, bool_t verbose_errors) +static void init_once_LOCKED( lua_State* L) { - GVerboseErrors = verbose_errors; + STACK_CHECK( L); + + lua_getfield( L, 1, "verbose_errors"); + GVerboseErrors = lua_toboolean( L, -1); + lua_pop( L, 1); + + STACK_MID( L, 0); + #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) now_secs(); // initialize 'now_secs()' internal offset #endif @@ -2657,26 +2673,27 @@ static void init_once_LOCKED( lua_State* L, int const _on_state_create, int cons #endif #if HAVE_LANE_TRACKING - tracking_first = _track_lanes ? TRACKING_END : NULL; + MUTEX_INIT( &tracking_cs); + lua_getfield( L, 1, "track_lanes"); + tracking_first = lua_toboolean( L, -1) ? TRACKING_END : NULL; + lua_pop( L, 1); + STACK_MID( L, 0); #endif // HAVE_LANE_TRACKING // Locks for 'tools.c' inc/dec counters // - MUTEX_INIT( &deep_lock ); - MUTEX_INIT( &mtid_lock ); + MUTEX_INIT( &deep_lock); + MUTEX_INIT( &mtid_lock); // Serialize calls to 'require' from now on, also in the primary state // - MUTEX_RECURSIVE_INIT( &require_cs ); + MUTEX_RECURSIVE_INIT( &require_cs); serialize_require( L); // Linked chains handling // - MUTEX_INIT( &selfdestruct_cs ); -#if HAVE_LANE_TRACKING - MUTEX_INIT( &tracking_cs); -#endif // HAVE_LANE_TRACKING + MUTEX_INIT( &selfdestruct_cs); //--- // Linux needs SCHED_RR to change thread priorities, and that is only @@ -2702,7 +2719,7 @@ static void init_once_LOCKED( lua_State* L, int const _on_state_create, int cons #endif // LINUX_SCHED_RR #endif // PLATFORM_LINUX { - char const* err = init_keepers( L, _on_state_create, nbKeepers); + char const* err = init_keepers( L); if (err) { (void) luaL_error( L, "Unable to initialize: %s", err ); @@ -2713,40 +2730,41 @@ static void init_once_LOCKED( lua_State* L, int const _on_state_create, int cons // ASSERT_L( timer_deep == NULL); - STACK_CHECK( L); - { - // proxy_ud= deep_userdata( idfunc ) - // - lua_pushliteral( L, "lanes-timer"); // push a name for debug purposes - luaG_deep_userdata( L, linda_id); - STACK_MID( L, 2); - lua_remove( L, -2); // remove the name as we no longer need it - - ASSERT_L( lua_isuserdata(L,-1) ); + // proxy_ud= deep_userdata( idfunc ) + // + lua_pushliteral( L, "lanes-timer"); // push a name for debug purposes + luaG_deep_userdata( L, linda_id); + STACK_MID( L, 2); + lua_remove( L, -2); // remove the name as we no longer need it - // 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( lua_isuserdata(L,-1)); - // 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. - // at the same time, use this object as a 'desinit' marker: - // when the main lua State is closed, this object will be GC'ed - { - lua_newuserdata( L, 1); - lua_newtable( L); - lua_pushnumber( L, _shutdown_timeout); - lua_pushcclosure( L, selfdestruct_gc, 1); - lua_setfield( L, -2, "__gc"); - lua_pushliteral( L, "AtExit"); - lua_setfield( L, -2, "__metatable"); - lua_setmetatable( L, -2); - } - lua_insert( L, -2); // Swap key with the Linda object - lua_rawset( L, LUA_REGISTRYINDEX); + // 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); + // 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. + // at the same time, use this object as a 'desinit' marker: + // when the main lua State is closed, this object will be GC'ed + { + lua_newuserdata( L, 1); + lua_newtable( L); + lua_getfield( L, 1, "shutdown_timeout"); + lua_pushcclosure( L, selfdestruct_gc, 1); + lua_setfield( L, -2, "__gc"); + lua_pushliteral( L, "AtExit"); + lua_setfield( L, -2, "__metatable"); + lua_setmetatable( L, -2); } + lua_insert( L, -2); // Swap key with the Linda object + lua_rawset( L, LUA_REGISTRYINDEX); + + // we'll need this everytime we transfer some C function from/to this state + lua_newtable( L); + lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); + STACK_END( L, 0); } @@ -2754,23 +2772,19 @@ static volatile long s_initCount = 0; // upvalue 1: module name // upvalue 2: module table +// param 1: settings 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); - int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; - lua_Number shutdown_timeout = lua_tonumber( L, 3); - bool_t track_lanes = lua_toboolean( L, 4); - bool_t protect_allocator = lua_toboolean( L, 5); - bool_t verbose_errors = lua_toboolean( L, 6); + _ASSERT_L( L, lua_type( L, 1) == LUA_TTABLE); + STACK_CHECK( L); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); DEBUGSPEW_CODE( ++ debugspew_indent_depth); - STACK_CHECK( L); // not in init_once_LOCKED because we can have several hosted "master" Lua states where Lanes is require()d. - if( protect_allocator) + lua_getfield( L, 1, "protect_allocator"); // settings protect_allocator + if( lua_toboolean( L, -1)) { void* ud; lua_Alloc allocf = lua_getallocf( L, &ud); @@ -2783,6 +2797,7 @@ LUAG_FUNC( configure) lua_setallocf( L, protected_lua_Alloc, s); } } + lua_pop( L, 1); // settings STACK_MID( L, 0); /* @@ -2797,7 +2812,7 @@ LUAG_FUNC( configure) static volatile int /*bool*/ go_ahead; // = 0 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) { - init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes, verbose_errors); + init_once_LOCKED( L); go_ahead = 1; // let others pass } else @@ -2815,7 +2830,7 @@ LUAG_FUNC( configure) // if( s_initCount == 0) { - init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes, verbose_errors); + init_once_LOCKED( L); s_initCount = 1; } } @@ -2824,56 +2839,64 @@ LUAG_FUNC( configure) #endif // THREADAPI == THREADAPI_PTHREAD // Retrieve main module interface table - lua_pushvalue( L, lua_upvalueindex( 2)); // ... M + lua_pushvalue( L, lua_upvalueindex( 2)); // settings M // remove configure() (this function) from the module interface - lua_pushnil( L); // ... M nil - lua_setfield( L, -2, "configure"); // ... 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"); + } +#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); // ... M timer_deep - lua_setfield( L, -2, "timer_gateway"); // ... M + luaG_push_proxy( L, linda_id, (DEEP_PRELUDE*) timer_deep); // settings M timer_deep + 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 } // - 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 + lua_newtable( L); // 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 ASSERT_L( lua_isfunction( L, -1)); - lua_setfield( L, -2, "cached_error"); // ... M mt - lua_getglobal( L, "tostring"); // ... 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"); // ... 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_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_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); // ... M LG_thread_new - lua_setfield(L, -2, "thread_new"); // ... 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"); // ... M require - lua_pushcclosure( L, LG_require, 1); // ... M lanes.require - lua_setfield( L, -2, "require"); // ... 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); // ... M VERSION - lua_setfield(L, -2, "version"); // ... M + lua_pushstring(L, VERSION); // settings M VERSION + lua_setfield(L, -2, "version"); // settings M - lua_pushinteger(L, THREAD_PRIO_MAX); // ... M THREAD_PRIO_MAX - lua_setfield(L, -2, "max_prio"); // ... 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); // ... M CANCEL_ERROR - lua_setfield(L, -2, "cancel_error"); // ... 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) @@ -2882,14 +2905,18 @@ LUAG_FUNC( configure) // 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 + lua_pushglobaltable( L); // settings M _G populate_func_lookup_table( L, -1, NULL); - lua_pop( L, 2); // ... + lua_pop( L, 1); // settings M + // 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 STACK_END( L, 0); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L)); DEBUGSPEW_CODE( -- debugspew_indent_depth); - // Return nothing - return 0; + // Return the settings table + return 1; } // helper to have correct callstacks when crashing a Win32 running on 64 bits Windows @@ -2920,16 +2947,28 @@ int LANES_API luaopen_lanes_core( lua_State* L) { EnableCrashingOnCrashes(); - STACK_GROW( L, 3); + STACK_GROW( L, 4); STACK_CHECK( L); // Create main module interface table // we only have 1 closure, which must be called to configure Lanes - lua_newtable( L); // M - lua_pushvalue( L, 1); // M "lanes.core" - lua_pushvalue( L, -2); // M "lanes.core" M - lua_pushcclosure( L, LG_configure, 2); // M LG_configure() - lua_setfield( L, -2, "configure"); // M + lua_newtable( L); // M + lua_pushvalue( L, 1); // M "lanes.core" + lua_pushvalue( L, -2); // M "lanes.core" M + lua_pushcclosure( L, LG_configure, 2); // M LG_configure() + lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); // M LG_configure() settings + if( !lua_isnil( L, -1)) // this is not the first require "lanes.core": call configure() immediately + { + lua_pushvalue( L, -1); // M LG_configure() settings settings + lua_setfield( L, -4, "settings"); // M LG_configure() settings + lua_call( L, 1, 0); // M + } + else + { + // will do nothing on first invocation, as we haven't stored settings in the registry yet + lua_setfield( L, -3, "settings"); // M LG_configure() + lua_setfield( L, -2, "configure"); // M + } STACK_END( L, 1); return 1; diff --git a/src/lanes.lua b/src/lanes.lua index 01ca181..0e27ea6 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -35,13 +35,15 @@ THE SOFTWARE. =============================================================================== ]]-- +local core = require "lanes.core" -- Lua 5.1: module() creates a global variable -- Lua 5.2: module() is gone -- almost everything module() does is done by require() anyway -- -> simply create a table, populate it, return it, and be done local lanes = {} -lanes.configure = function( _params) +-- this function is available in the public interface until it is called, after which it disappears +lanes.configure = function( settings_) -- This check is for sublanes requiring Lanes -- @@ -68,7 +70,7 @@ lanes.configure = function( _params) on_state_create = nil, shutdown_timeout = 0.25, with_timers = true, - track_lanes = nil, + track_lanes = false, verbose_errors = false, -- LuaJIT provides a thread-unsafe allocator by default, so we need to protect it when used in parallel lanes protect_allocator = (jit and jit.version) and true or false @@ -117,35 +119,32 @@ lanes.configure = function( _params) end } - local params_checker = function( _params) - if not _params then + local params_checker = function( settings_) + if not settings_ then return default_params end - if type( _params) ~= "table" then - error( "Bad parameter #1 to lanes.configure(), should be a table") + -- make a copy of the table to leave the provided one unchanged, *and* to help ensure it won't change behind our back + local settings = {} + if type( settings_) ~= "table" then + error "Bad parameter #1 to lanes.configure(), should be a table" end -- any setting not present in the provided parameters takes the default value - for key, value in pairs( default_params) do - local my_param = _params[key] + for key, checker in pairs( param_checkers) do + local my_param = settings_[key] local param if my_param ~= nil then param = my_param else param = default_params[key] end - if not param_checkers[key]( param) then + if not checker( param) then error( "Bad " .. key .. ": " .. tostring( param), 2) end - _params[key] = param + settings[key] = param end - return _params + return settings end - - _params = params_checker( _params) - - local core = require "lanes.core" - assert( type( core)=="table") - core.configure( _params.nb_keepers, _params.on_state_create, _params.shutdown_timeout, _params.track_lanes, _params.protect_allocator, _params.verbose_errors) + local settings = core.configure and core.configure( params_checker( settings_)) or core.settings local thread_new = assert( core.thread_new) local set_singlethreaded = assert( core.set_singlethreaded) local max_prio = assert( core.max_prio) @@ -316,8 +315,8 @@ local function gen( ... ) -- Lane generator -- return function(...) - return thread_new( func, libs, _params.on_state_create, cs, prio, g_tbl, package_tbl, required, ...) -- args - end + return thread_new( func, libs, settings.on_state_create, cs, prio, g_tbl, package_tbl, required, ...) -- args + end end ---=== Lindas ===--- @@ -338,7 +337,7 @@ local timer = function() error "timers are not active" end local timer_lane = nil local timers = timer -if _params.with_timers ~= false then +if settings.with_timers ~= false then local timer_gateway = assert( core.timer_gateway) -- @@ -364,6 +363,8 @@ timer_gateway:set(first_time_key,true) -- if first_time then + local now_secs = core.now_secs + assert( type( now_secs) == "function") ----- -- Snore loop (run as a lane on the background) -- @@ -373,10 +374,7 @@ if first_time then -- remains. -- local timer_body = function() - -- require lanes.core inside the timer body to prevent pulling now_secs() through an uvpvalue - local core = require "lanes.core" - core.configure( _params.nb_keepers, _params.on_state_create, _params.shutdown_timeout, _params.track_lanes, _params.protect_allocator, _params.verbose_errors) - + set_debug_threadname( "LanesTimer") -- -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, -- [key]= { wakeup_secs [,period_secs] } [, ...] }, @@ -463,8 +461,6 @@ if first_time then end end -- set_timer() - local now_secs = core.now_secs - assert( type( now_secs) == "function") ----- -- [next_wakeup_at]= check_timers() -- Check timers, and wake up the ones expired (if any) @@ -520,7 +516,6 @@ if first_time then end -- check_timers() local timer_gateway_batched = timer_gateway.batched - set_debug_threadname( "LanesTimer") set_finalizer( function( err, stk) if err and type( err) ~= "userdata" then WR( "LanesTimer error: "..tostring(err)) @@ -601,7 +596,7 @@ timers = function() return r end -end -- _params.with_timers +end -- settings.with_timers ---=== Lock & atomic generators ===--- @@ -673,26 +668,23 @@ end lanes.linda = core.linda lanes.cancel_error = core.cancel_error lanes.nameof = core.nameof - lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end + lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false lanes.timer = timer lanes.timer_lane = timer_lane lanes.timers = timers lanes.genlock = genlock lanes.now_secs = core.now_secs lanes.genatomic = genatomic - -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation - lanes.configure = function( _params2) - _params2 = params_checker( _params2 or _params) - for key, value2 in pairs( _params2) do - local value = _params[key] - if value2 ~= value then - error( "mismatched configuration: " .. key .. " is " .. tostring( value2) .. " instead of " .. tostring( value)) - end - end - return lanes - end + lanes.configure = nil -- no need to call configure() ever again return lanes end -- lanes.configure +-- no need to force calling configure() excepted the first time +if core.settings then + return lanes.configure() +else + return lanes +end + --the end return lanes diff --git a/src/threading.c b/src/threading.c index 1e4fc92..42c0cde 100644 --- a/src/threading.c +++ b/src/threading.c @@ -84,8 +84,9 @@ THE SOFTWARE. * error in _this_ code. */ #if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) -static void FAIL( const char *funcname, int rc ) { - fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); +static void FAIL( char const* funcname, int rc) +{ + fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); #ifdef _MSC_VER __debugbreak(); // give a chance to the debugger! #endif // _MSC_VER diff --git a/src/threading.h b/src/threading.h index 7d94f26..d2320aa 100644 --- a/src/threading.h +++ b/src/threading.h @@ -97,7 +97,7 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; #define SIGNAL_T CONDITION_VARIABLE #define MUTEX_T CRITICAL_SECTION #define MUTEX_INIT( ref) InitializeCriticalSection( ref) - #define MUTEX_FREE( ref) DeleteCriticalSection( ref) + #define MUTEX_FREE( ref) do{ DeleteCriticalSection( ref); *ref = INVALID_HANDLE_VALUE;} while(0) #define MUTEX_LOCK( ref) EnterCriticalSection( ref) #define MUTEX_UNLOCK( ref) LeaveCriticalSection( ref) diff --git a/src/tools.c b/src/tools.c index b02177b..95104d6 100644 --- a/src/tools.c +++ b/src/tools.c @@ -44,9 +44,31 @@ THE SOFTWARE. #include #include +/* + * ############################################################################################### + * ########################################### ASSERT ############################################ + * ############################################################################################### + */ +void ASSERT_IMPL( lua_State* L, bool_t cond_, char const* file_, int const line_, char const* text_) +{ + if ( !cond_) + { + (void) luaL_error( L, "ASSERT failed: %s:%d '%s'", file_, line_, text_); + } +} + // for verbose errors bool_t GVerboseErrors = FALSE; +char const* const CONFIG_REGKEY = "ee932492-a654-4506-9da8-f16540bdb5d4"; +char const* const LOOKUP_REGKEY = "ddea37aa-50c7-4d3f-8e0b-fb7a9d62bac5"; + +/* + * ############################################################################################### + * ######################################### Lua 5.1/5.2 ######################################### + * ############################################################################################### + */ + /* ** Copied from Lua 5.2 loadlib.c */ @@ -137,6 +159,19 @@ void luaG_dump( lua_State* L ) { fprintf( stderr, "\n" ); } +// just like lua_xmove, args are (from, to) +void luaG_copy_one_time_settings( lua_State* L, lua_State* L2, char const* name_) +{ + 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_); + } + lua_setfield( L2, LUA_REGISTRYINDEX, CONFIG_REGKEY); +} + /*---=== luaG_newstate ===---*/ @@ -171,24 +206,31 @@ static const luaL_Reg libs[] = { NULL, NULL } }; -static void open1lib( lua_State* L, char const* name, size_t len) +static void open1lib( lua_State* L, char const* name_, size_t len_, lua_State* from_) { int i; for( i = 0; libs[i].name; ++ i) { - if( strncmp( name, libs[i].name, len) == 0) + if( strncmp( name_, libs[i].name, len_) == 0) { lua_CFunction libfunc = libs[i].func; - if( libfunc) + name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_ + if( libfunc != NULL) { - bool_t createGlobal = (libfunc != require_lanes_core) ? TRUE : FALSE; // don't want to create a global for "lanes.core" - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END, len, name)); + bool_t const isLanesCore = (libfunc == require_lanes_core) ? TRUE : FALSE; // don't want to create a global for "lanes.core" + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END, len_, name_)); STACK_CHECK( L); + if( isLanesCore == TRUE) + { + // copy settings from from source to destination registry + luaG_copy_one_time_settings( from_, L, name_); + } // 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, libs[i].name, libfunc, createGlobal); - if( createGlobal == FALSE) + luaL_requiref( L, name_, libfunc, !isLanesCore); + // lanes.core doesn't declare a global, so scan it here and now + if( isLanesCore == TRUE) { - populate_func_lookup_table( L, -1, name); + populate_func_lookup_table( L, -1, name_); } lua_pop( L, 1); STACK_END( L, 0); @@ -473,14 +515,8 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) DEBUGSPEW_CODE( ++ debugspew_indent_depth); STACK_GROW( L, 3); STACK_CHECK( L); - lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}? - if( lua_isnil( L, -1)) // nil - { - lua_pop( L, 1); // - lua_newtable( L); // {} - lua_pushvalue( L, -1); // {} {} - lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} - } + lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {} + ASSERT_L( lua_istable( L, -1)); if( lua_type( L, in_base) == LUA_TFUNCTION) // for example when a module is a simple function { name_ = name_ ? name_ : "NULL"; @@ -538,17 +574,18 @@ lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char con lua_Alloc allocF = lua_getallocf( _from, &allocUD); lua_State* L = lua_newstate( allocF, allocUD); - if( !L) + if( L == NULL) { - luaL_error( _from, "'lua_newstate()' failed; out of memory"); + (void) luaL_error( _from, "'lua_newstate()' failed; out of memory"); } // neither libs (not even 'base') nor special init func: we are done if( libs == NULL && _on_state_create <= 0) { + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(NULL)\n" INDENT_END)); return L; } - // if we are here, no keeper state is involved (because libs == NULL when we init keepers) + // from this point, we are not creating a keeper state (because libs == NULL when we init keepers) DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END)); DEBUGSPEW_CODE( ++ debugspew_indent_depth); @@ -556,12 +593,15 @@ lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char con STACK_GROW( L, 2); STACK_CHECK( L); // 'lua.c' stops GC during initialization so perhaps its a good idea. :) - // but do it after _on_state_create in case it does a lot of stuff... lua_gc( L, LUA_GCSTOP, 0); + // we'll need this everytime we transfer some C function from/to this state + lua_newtable( L); + lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); + // Anything causes 'base' to be taken in // - if( libs) + if( libs != NULL) { // special "*" case (mainly to help with LuaJIT compatibility) // as we are called from luaopen_lanes_core() already, and that would deadlock @@ -570,7 +610,7 @@ lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char con 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); + open1lib( L, "lanes.core", 10, _from); libs = NULL; // done with libs } else @@ -604,7 +644,7 @@ lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char con while( isalnum( p[len]) || p[len] == '.') ++ len; // open library - open1lib( L, p, len); + open1lib( L, p, len, _from); } serialize_require( L); } @@ -612,7 +652,7 @@ lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char con lua_gc( L, LUA_GCRESTART, 0); STACK_CHECK( L); - // call this after the base libraries are loaded! + // call this after the base libraries are loaded and GC is restarted if( _on_state_create > 0) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); @@ -633,7 +673,7 @@ lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char con // capture error and forward it to main state if( lua_pcall( L, 0, 0, 0) != LUA_OK) { - luaL_error( _from, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1))); + (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); } @@ -1486,7 +1526,7 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, enum eLoo else { // fetch the name from the source state's lookup table - lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // ... f ... {} + lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // ... f ... {} ASSERT_L( lua_istable( L, -1)); lua_pushvalue( L, i); // ... f ... {} f lua_rawget( L, -2); // ... f ... {} "f.q.n" @@ -1533,7 +1573,7 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, enum eLoo } else { - lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} + lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {} ASSERT_L( lua_istable( L2, -1)); lua_pushlstring( L2, fqn, len); // {} "f.q.n" lua_rawget( L2, -2); // {} f @@ -1923,7 +1963,7 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u */ if( inter_copy_one_( L2, L2_cache_i, L, val_i, VT_NORMAL, mode_, valPath)) { - ASSERT_L( lua_istable(L2,-3)); + ASSERT_L( lua_istable( L2, -3)); lua_rawset( L2, -3); // add to table (pops key & val) } else diff --git a/src/tools.h b/src/tools.h index 1113131..6d3b3c7 100644 --- a/src/tools.h +++ b/src/tools.h @@ -56,7 +56,8 @@ extern int debugspew_indent_depth; #define STACK_END(L,c) /*nothing*/ #define STACK_DUMP(L) /*nothing*/ #else - #define _ASSERT_L(lua,c) do { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } while( 0) + void ASSERT_IMPL( lua_State* L, bool_t cond_, char const* file_, int const line_, char const* text_); + #define _ASSERT_L(lua,c) ASSERT_IMPL( lua, (c) != 0, __FILE__, __LINE__, #c) // #define STACK_CHECK(L) { int const _oldtop_##L = lua_gettop( L) #define STACK_MID(L,change) \ @@ -75,7 +76,7 @@ extern int debugspew_indent_depth; #define STACK_GROW(L,n) do { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } while( 0) -#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L ) +#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State* L) #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) @@ -83,6 +84,7 @@ extern int debugspew_indent_depth; void luaG_dump( lua_State* L ); lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs); +void luaG_copy_one_time_settings( lua_State* L, lua_State* L2, char const* name_); typedef struct { volatile int refcount; @@ -117,5 +119,7 @@ extern MUTEX_T require_cs; // for verbose errors extern bool_t GVerboseErrors; -#endif - // TOOLS_H +char const* const CONFIG_REGKEY; +char const* const LOOKUP_REGKEY; + +#endif // TOOLS_H -- cgit v1.2.3-55-g6feb