From e0dbd33c2d4776d6b2213dd82f344166eafde438 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 1 Aug 2017 09:40:44 +0200 Subject: Fix for deep-aware modules Don't crash when using a module that creates Lanes-compatible deep userdata. Added a sample deep-aware module. --- src/Makefile | 2 +- src/deep.c | 61 +++++++++++++------------ src/keeper.c | 8 ++-- src/lanes.c | 45 +++++++++---------- src/macros_and_utils.h | 71 +++++++++++++++++++++++++++++ src/tools.c | 48 +++++--------------- src/tools.h | 118 ++----------------------------------------------- src/universe.c | 74 +++++++++++++++++++++++++++++++ src/universe.h | 66 +++++++++++++++++++++++++++ 9 files changed, 284 insertions(+), 209 deletions(-) create mode 100644 src/macros_and_utils.h create mode 100644 src/universe.c create mode 100644 src/universe.h (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 7e45d2a..37b5a37 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,7 +7,7 @@ MODULE=lanes -SRC=lanes.c compat.c threading.c tools.c deep.c keeper.c +SRC=lanes.c compat.c threading.c tools.c deep.c keeper.c universe.c OBJ=$(SRC:.c=.o) diff --git a/src/deep.c b/src/deep.c index b5d6aee..71a798c 100644 --- a/src/deep.c +++ b/src/deep.c @@ -1,7 +1,7 @@ /* - * DEEP.C Copyright (c) 2014, Benoit Germain + * DEEP.C Copyright (c) 2017, Benoit Germain * - * Depp userdata support, separate in its own source file to help integration + * Deep userdata support, separate in its own source file to help integration * without enforcing a Lanes dependency */ @@ -9,7 +9,7 @@ =============================================================================== Copyright (C) 2002-10 Asko Kauppi - 2011-14 Benoit Germain + 2011-17 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 @@ -34,6 +34,7 @@ THE SOFTWARE. #include "compat.h" #include "tools.h" +#include "universe.h" #include "deep.h" #include @@ -120,14 +121,15 @@ void luaG_pushdeepversion( lua_State* L) { (void) lua_pushliteral( L, "f248e77a- * metatable -> idfunc * idfunc -> metatable */ -#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup) - // any unique light userdata +// crc64/we of string "DEEP_LOOKUP_KEY" generated at https://www.nitrxgen.net/hashgen/ +#define DEEP_LOOKUP_KEY ((void*)0x9fb9b4f3f633d83d) /* -* The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying + * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying + * crc64/we of string "DEEP_PROXY_CACHE_KEY" generated at https://www.nitrxgen.net/hashgen/ */ -#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy) +#define DEEP_PROXY_CACHE_KEY ((void*)0x05773d6fc26be106) /* * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. @@ -163,7 +165,7 @@ static void get_deep_lookup( lua_State* L) { lua_insert( L, -2); // {} a lua_rawget( L, -2); // {} b - } + } lua_remove( L, -2); // a|b STACK_END( L, 0); } @@ -177,7 +179,7 @@ static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupM // when looking inside a keeper, we are 100% sure the object is a deep userdata if( mode_ == eLM_FromKeeper) { - DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index); + struct DEEP_PRELUDE** proxy = (struct DEEP_PRELUDE**) lua_touserdata( L, index); // we can (and must) cast and fetch the internally stored idfunc return (*proxy)->idfunc; } @@ -206,7 +208,7 @@ static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupM } -void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_) +void free_deep_prelude( lua_State* L, struct DEEP_PRELUDE* prelude_) { // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup lua_pushlightuserdata( L, prelude_->deep); @@ -224,16 +226,18 @@ void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_) */ static int deep_userdata_gc( lua_State* L) { - DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1); - DEEP_PRELUDE* p = *proxy; - struct s_Universe* U = get_universe( L); + struct DEEP_PRELUDE** proxy = (struct DEEP_PRELUDE**) lua_touserdata( L, 1); + struct DEEP_PRELUDE* p = *proxy; + struct s_Universe* U = universe_get( L); int v; *proxy = 0; // make sure we don't use it any more - MUTEX_LOCK( &U->deep_lock); + // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded + // in that case, we are not multithreaded and locking isn't necessary anyway + if( U) MUTEX_LOCK( &U->deep_lock); v = -- (p->refcount); - MUTEX_UNLOCK( &U->deep_lock); + if (U) MUTEX_UNLOCK( &U->deep_lock); if( v == 0) { @@ -260,9 +264,9 @@ static int deep_userdata_gc( lua_State* L) * used in this Lua state (metatable, registring it). Otherwise, increments the * reference count. */ -char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_) +char const* push_deep_proxy( struct s_Universe* U, lua_State* L, struct DEEP_PRELUDE* prelude, enum eLookupMode mode_) { - DEEP_PRELUDE** proxy; + struct DEEP_PRELUDE** proxy; // Check if a proxy already exists push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC @@ -278,14 +282,16 @@ char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* p lua_pop( L, 1); // DPC } - MUTEX_LOCK( &U->deep_lock); + // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded + // in that case, we are not multithreaded and locking isn't necessary anyway + if( U) MUTEX_LOCK( &U->deep_lock); ++ (prelude->refcount); // one more proxy pointing to this deep data - MUTEX_UNLOCK( &U->deep_lock); + if( U) MUTEX_UNLOCK( &U->deep_lock); STACK_GROW( L, 7); STACK_CHECK( L); - proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy + proxy = lua_newuserdata( L, sizeof(struct DEEP_PRELUDE*)); // DPC proxy ASSERT_L( proxy); *proxy = prelude; @@ -301,7 +307,7 @@ char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* p // 1 - make one and register it if( mode_ != eLM_ToKeeper) { - prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion + (void) prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion if( lua_gettop( L) - oldtop != 1 || !lua_istable( L, -2) || !lua_isstring( L, -1)) { lua_settop( L, oldtop); // DPC proxy X @@ -350,7 +356,7 @@ char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* p return "Bad idfunc(eOP_module): should not push anything"; } } - if( modname) // we actually got a module name + if( NULL != modname) // we actually got a module name { // somehow, L.registry._LOADED can exist without having registered the 'package' library. lua_getglobal( L, "require"); // DPC proxy metatable require() @@ -413,7 +419,6 @@ char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* p return NULL; } - /* * Create a deep userdata * @@ -439,7 +444,7 @@ char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* p int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) { char const* errmsg; - DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE)); + struct DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(struct DEEP_PRELUDE)); if( prelude == NULL) { return luaL_error( L, "couldn't not allocate deep prelude: out of memory"); @@ -463,7 +468,7 @@ int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack"); } } - errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy + errmsg = push_deep_proxy( universe_get( L), L, prelude, eLM_LaneBody); // proxy if( errmsg != NULL) { luaL_error( L, errmsg); @@ -481,7 +486,7 @@ int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) */ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) { - DEEP_PRELUDE** proxy; + struct DEEP_PRELUDE** proxy; STACK_CHECK( L); // ensure it is actually a deep userdata @@ -490,7 +495,7 @@ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) return NULL; // no metatable, or wrong kind } - proxy = (DEEP_PRELUDE**) lua_touserdata( L, index); + proxy = (struct DEEP_PRELUDE**) lua_touserdata( L, index); STACK_END( L, 0); return (*proxy)->deep; @@ -513,7 +518,7 @@ luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int return NULL; // not a deep userdata } - errmsg = push_deep_proxy( U, L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_); + errmsg = push_deep_proxy( U, L2, *(struct DEEP_PRELUDE**) lua_touserdata( L, index), mode_); if( errmsg != NULL) { // raise the error in the proper state (not the keeper) diff --git a/src/keeper.c b/src/keeper.c index 9a7c804..dbf083f 100644 --- a/src/keeper.c +++ b/src/keeper.c @@ -46,6 +46,7 @@ #include "threading.h" #include "compat.h" #include "tools.h" +#include "universe.h" #include "keeper.h" //################################################################################### @@ -664,14 +665,11 @@ void init_keepers( struct s_Universe* U, lua_State* L) // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. // therefore, we need a recursive mutex. MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs); - STACK_CHECK( K); // copy the universe pointer in the keeper itself - lua_pushlightuserdata( K, UNIVERSE_REGKEY); - lua_pushlightuserdata( K, U); - lua_rawset( K, LUA_REGISTRYINDEX); - STACK_MID( K, 0); + universe_store( K, U); + STACK_CHECK( K); // make sure 'package' is initialized in keeper states, so that we have require() // this because this is needed when transferring deep userdata object luaL_requiref( K, "package", luaopen_package, 1); // package diff --git a/src/lanes.c b/src/lanes.c index 78fece8..3268c8b 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -52,7 +52,7 @@ * ... */ -char const* VERSION = "3.11"; +char const* VERSION = "3.12"; /* =============================================================================== @@ -89,6 +89,7 @@ THE SOFTWARE. #include "threading.h" #include "compat.h" #include "tools.h" +#include "universe.h" #include "keeper.h" #include "lanes.h" @@ -1070,7 +1071,7 @@ LUAG_FUNC( linda_concat) LUAG_FUNC( linda_dump) { struct s_Linda* linda = lua_toLinda( L, 1); - ASSERT_L( linda->U == get_universe( L)); + ASSERT_L( linda->U == universe_get( L)); return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); } @@ -1082,7 +1083,7 @@ LUAG_FUNC( linda_towatch) { struct s_Linda* linda = lua_toLinda( L, 1); int pushed; - ASSERT_L( linda->U == get_universe( L)); + ASSERT_L( linda->U == universe_get( L)); pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); if( pushed == 0) { @@ -1159,7 +1160,7 @@ static void* linda_id( lua_State* L, enum eDeepOp op_) { SIGNAL_INIT( &s->read_happened); SIGNAL_INIT( &s->write_happened); - s->U = get_universe( L); + s->U = universe_get( L); s->simulate_cancel = CANCEL_NONE; s->group = linda_group << KEEPER_MAGIC_SHIFT; s->name[0] = 0; @@ -1718,7 +1719,7 @@ static int selfdestruct_gc( lua_State* L) lua_settop( L, 0); // no need to mutex-protect this as all threads in the universe are gone at that point -- U->timer_deep->refcount; // should be 0 now - free_deep_prelude( L, (DEEP_PRELUDE*) U->timer_deep); + free_deep_prelude( L, (struct DEEP_PRELUDE*) U->timer_deep); U->timer_deep = NULL; close_keepers( U, L); @@ -2045,7 +2046,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) lua_State* L = s->L; // Called with the lane function and arguments on the stack int const nargs = lua_gettop( L) - 1; - DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* U = universe_get( L)); THREAD_MAKE_ASYNCH_CANCELLABLE(); THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); s->status = RUNNING; // PENDING -> RUNNING @@ -2143,7 +2144,7 @@ LUAG_FUNC( require) { char const* name = lua_tostring( L, 1); int const nargs = lua_gettop( L); - DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* U = universe_get( L)); STACK_CHECK( L); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); @@ -2169,7 +2170,7 @@ LUAG_FUNC( register) // ignore extra parameters, just in case lua_settop( L, 2); luaL_argcheck( L, (mod_type == LUA_TTABLE) || (mod_type == LUA_TFUNCTION), 2, "unexpected module type"); - DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* U = universe_get( L)); STACK_CHECK( L); // "name" mod_table DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); @@ -2212,7 +2213,7 @@ LUAG_FUNC( lane_new) #define FIXED_ARGS 8 int const nargs = lua_gettop(L) - FIXED_ARGS; - struct s_Universe* U = get_universe( L); + struct s_Universe* U = universe_get( L); ASSERT_L( nargs >= 0); // public Lanes API accepts a generic range -3/+3 @@ -2660,7 +2661,7 @@ LUAG_FUNC( thread_join) } else { - struct s_Universe* U = get_universe( L); + struct s_Universe* U = universe_get( L); // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed // so store it in the userdata uservalue at a key that can't possibly collide securize_debug_threadname( L, s); @@ -2872,7 +2873,7 @@ LUAG_FUNC( thread_index) LUAG_FUNC( threads) { int const top = lua_gettop( L); - struct s_Universe* U = get_universe( L); + struct s_Universe* U = universe_get( L); // List _all_ still running threads // @@ -3024,7 +3025,7 @@ static volatile long s_initCount = 0; // param 1: settings table LUAG_FUNC( configure) { - struct s_Universe* U = get_universe( L); + struct s_Universe* U = universe_get( L); bool_t const from_master_state = (U == NULL); char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); _ASSERT_L( L, lua_type( L, 1) == LUA_TTABLE); @@ -3093,16 +3094,14 @@ LUAG_FUNC( configure) // grab or create the universe if( U == NULL) { - lua_pushlightuserdata( L, UNIVERSE_REGKEY); // settings UNIVERSE_REGKEY - U = (struct s_Universe*) lua_newuserdata( L, sizeof( struct s_Universe)); // settings UNIVERSE_REGKEY universe - memset( U, 0, sizeof( struct s_Universe)); + U = universe_create( L); // settings universe DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - lua_newtable( L); // settings UNIVERSE_REGKEY universe mt - lua_getfield( L, 1, "shutdown_timeout"); // settings UNIVERSE_REGKEY universe mt shutdown_timeout - lua_pushcclosure( L, selfdestruct_gc, 1); // settings UNIVERSE_REGKEY universe mt selfdestruct_gc - lua_setfield( L, -2, "__gc"); // settings UNIVERSE_REGKEY universe mt - lua_setmetatable( L, -2); // settings UNIVERSE_REGKEY universe - lua_rawset( L, LUA_REGISTRYINDEX); // settings + lua_newtable( L); // settings universe mt + lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout + lua_pushcclosure( L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc + lua_setfield( L, -2, "__gc"); // settings universe mt + lua_setmetatable( L, -2); // settings universe + lua_pop( L, 1); // settings lua_getfield( L, 1, "verbose_errors"); // settings verbose_errors U->verboseErrors = lua_toboolean( L, -1); lua_pop( L, 1); // settings @@ -3130,7 +3129,7 @@ LUAG_FUNC( configure) STACK_MID( L, 1); // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer - U->timer_deep = * (DEEP_PRELUDE**) lua_touserdata( L, -1); + U->timer_deep = *(struct DEEP_PRELUDE**) lua_touserdata( L, -1); ASSERT_L( U->timer_deep && (U->timer_deep->refcount == 1) && U->timer_deep->deep && U->timer_deep->idfunc == linda_id); // increment refcount that this linda remains alive as long as the universe is. ++ U->timer_deep->refcount; @@ -3160,7 +3159,7 @@ LUAG_FUNC( configure) { char const* errmsg; - errmsg = push_deep_proxy( U, L, (DEEP_PRELUDE*) U->timer_deep, eLM_LaneBody); // settings M timer_deep + errmsg = push_deep_proxy( U, L, (struct DEEP_PRELUDE*) U->timer_deep, eLM_LaneBody);// settings M timer_deep if( errmsg != NULL) { return luaL_error( L, errmsg); diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h new file mode 100644 index 0000000..d584c2b --- /dev/null +++ b/src/macros_and_utils.h @@ -0,0 +1,71 @@ +/* + * MACROS_AND_UTILS.H + */ +#ifndef MACROS_AND_UTILS_H +#define MACROS_AND_UTILS_H + +#include "lua.h" + + // M$ compiler doesn't support 'inline' keyword in C files... +#if defined( _MSC_VER) +#define inline __inline +#endif + + // For some reason, LuaJIT 64bits doesn't support lua_newstate() +#if defined(LUA_JITLIBNAME) && (defined(__x86_64__) || defined(_M_X64)) + //#pragma message( "LuaJIT 64 bits detected: don't propagate allocf") +#define PROPAGATE_ALLOCF 0 +#else // LuaJIT x64 + //#pragma message( "PUC-Lua detected: propagate allocf") +#define PROPAGATE_ALLOCF 1 +#endif // LuaJIT x64 +#if PROPAGATE_ALLOCF +#define PROPAGATE_ALLOCF_PREP( L) void* allocUD; lua_Alloc allocF = lua_getallocf( L, &allocUD) +#define PROPAGATE_ALLOCF_ALLOC() lua_newstate( allocF, allocUD) +#else // PROPAGATE_ALLOCF +#define PROPAGATE_ALLOCF_PREP( L) +#define PROPAGATE_ALLOCF_ALLOC() luaL_newstate() +#endif // PROPAGATE_ALLOCF + +#define USE_DEBUG_SPEW 0 +#if USE_DEBUG_SPEW +extern char const* debugspew_indent; +#define INDENT_BEGIN "%.*s " +#define INDENT_END , (U ? U->debugspew_indent_depth : 0), 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*/ + +#else // NDEBUG + +#define _ASSERT_L( L, cond_) if( (cond_) == 0) { (void) luaL_error( L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);} + +#define STACK_CHECK(L) { int const _oldtop_##L = lua_gettop( L) +#define STACK_MID(L,change) \ + do \ + { \ + int a = lua_gettop( L) - _oldtop_##L; \ + int b = (change); \ + if( a != b) \ + luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", a, b, __FILE__, __LINE__ ); \ + } while( 0) +#define STACK_END(L,change) STACK_MID(L,change); } + +#define STACK_DUMP( L) luaG_dump( L) + +#endif // NDEBUG + +#define ASSERT_L(c) _ASSERT_L(L,c) + +#define STACK_GROW( L, n) do { if (!lua_checkstack(L,(int)(n))) luaL_error( L, "Cannot grow stack!" ); } while( 0) + +#endif // MACROS_AND_UTILS_H diff --git a/src/tools.c b/src/tools.c index c2491ba..f935c26 100644 --- a/src/tools.c +++ b/src/tools.c @@ -32,6 +32,7 @@ THE SOFTWARE. */ #include "compat.h" +#include "universe.h" #include "tools.h" #include "keeper.h" #include "lanes.h" @@ -48,21 +49,6 @@ THE SOFTWARE. extern luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_); extern void push_registry_subtable( lua_State* L, void* key_); -void* const UNIVERSE_REGKEY = (void*) luaopen_lanes_core; - -/* - * ############################################################################################### - * ########################################### 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_); - } -} - char const* const CONFIG_REGKEY = "ee932492-a654-4506-9da8-f16540bdb5d4"; char const* const LOOKUP_REGKEY = "ddea37aa-50c7-4d3f-8e0b-fb7a9d62bac5"; @@ -150,19 +136,7 @@ void initialize_on_state_create( struct s_Universe* U, lua_State* L) STACK_END( L, 0); } - -struct s_Universe* get_universe( lua_State* L) -{ - struct s_Universe* universe; - STACK_GROW( L, 2); - STACK_CHECK( L); - lua_pushlightuserdata( L, UNIVERSE_REGKEY); - lua_rawget( L, LUA_REGISTRYINDEX); - universe = lua_touserdata( L, -1); // NULL if nil - lua_pop( L, 1); - STACK_END( L, 0); - return universe; -} +// ################################################################################################ // just like lua_xmove, args are (from, to) void luaG_copy_one_time_settings( struct s_Universe* U, lua_State* L, lua_State* L2) @@ -367,7 +341,7 @@ static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) size_t prevNameLength, newNameLength; char const* prevName; DEBUGSPEW_CODE( char const *newName); - DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* U = universe_get( L)); STACK_CHECK( L); // first, raise an error if the function is already known @@ -438,7 +412,7 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _ int const cache = _ctx_base + 2; // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) int const breadth_first_cache = lua_gettop( L) + 1; - DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* U = universe_get( L)); STACK_GROW( L, 6); // slot _i contains a table where we search for functions (or a full userdata with a metatable) @@ -556,7 +530,7 @@ 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 start_depth = 0; - DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* U = universe_get( L)); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "NULL")); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); STACK_GROW( L, 3); @@ -670,14 +644,12 @@ lua_State* luaG_newstate( struct s_Universe* U, lua_State* from_, char const* li } STACK_GROW( L, 2); - STACK_CHECK( L); // copy the universe as a light userdata (only the master state holds the full userdata) // that way, if Lanes is required in this new state, we'll know we are part of this universe - lua_pushlightuserdata( L, UNIVERSE_REGKEY); - lua_pushlightuserdata( L, U); - lua_rawset( L, LUA_REGISTRYINDEX); - STACK_MID( L, 0); + universe_store( L, U); + + STACK_CHECK( L); // we'll need this every time we transfer some C function from/to this state lua_newtable( L); @@ -858,7 +830,7 @@ static int table_lookup_sentinel( lua_State* L) */ static char const* find_lookup_name( lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_, size_t* len_) { - DEBUGSPEW_CODE( struct s_Universe* const U = get_universe( L)); + DEBUGSPEW_CODE( struct s_Universe* const U = universe_get( L)); char const* fqn; ASSERT_L( lua_isfunction( L, i) || lua_istable( L, i)); // ... v ... STACK_CHECK( L); @@ -1950,7 +1922,7 @@ int luaG_new_require( lua_State* L) { int rc, i; int args = lua_gettop( L); - struct s_Universe* U = get_universe( L); + struct s_Universe* U = universe_get( L); //char const* modname = luaL_checkstring( L, 1); STACK_GROW( L, args + 1); diff --git a/src/tools.h b/src/tools.h index b869a16..9155747 100644 --- a/src/tools.h +++ b/src/tools.h @@ -11,131 +11,21 @@ #include -// M$ compiler doesn't support 'inline' keyword in C files... -#if defined( _MSC_VER) -#define inline __inline -#endif - -// For some reason, LuaJIT 64bits doesn't support lua_newstate() -#if defined(LUA_JITLIBNAME) && (defined(__x86_64__) || defined(_M_X64)) -//#pragma message( "LuaJIT 64 bits detected: don't propagate allocf") -#define PROPAGATE_ALLOCF 0 -#else // LuaJIT x64 -//#pragma message( "PUC-Lua detected: propagate allocf") -#define PROPAGATE_ALLOCF 1 -#endif // LuaJIT x64 -#if PROPAGATE_ALLOCF -#define PROPAGATE_ALLOCF_PREP( L) void* allocUD; lua_Alloc allocF = lua_getallocf( L, &allocUD) -#define PROPAGATE_ALLOCF_ALLOC() lua_newstate( allocF, allocUD) -#else // PROPAGATE_ALLOCF -#define PROPAGATE_ALLOCF_PREP( L) -#define PROPAGATE_ALLOCF_ALLOC() luaL_newstate() -#endif // PROPAGATE_ALLOCF - -#define USE_DEBUG_SPEW 0 -#if USE_DEBUG_SPEW -extern char const* debugspew_indent; -#define INDENT_BEGIN "%.*s " -#define INDENT_END , (U ? U->debugspew_indent_depth : 0), debugspew_indent -#define DEBUGSPEW_CODE(_code) _code -#else // USE_DEBUG_SPEW -#define DEBUGSPEW_CODE(_code) -#endif // USE_DEBUG_SPEW - -// ################################################################################################ - -/* - * Do we want to activate full lane tracking feature? (EXPERIMENTAL) - */ -#define HAVE_LANE_TRACKING 1 +#include "macros_and_utils.h" // ################################################################################################ // this is pointed to by full userdata proxies, and allocated with malloc() to survive any lua_State lifetime -typedef struct +struct DEEP_PRELUDE { volatile int refcount; void* deep; // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc luaG_IdFunction idfunc; -} DEEP_PRELUDE; - -// ################################################################################################ - -// everything regarding the a Lanes universe is stored in that global structure -// held as a full userdata in the master Lua state that required it for the first time -// don't forget to initialize all members in LG_configure() -struct s_Universe -{ - // for verbose errors - bool_t verboseErrors; - - lua_CFunction on_state_create_func; - - struct s_Keepers* keepers; - - // 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 - -#if HAVE_LANE_TRACKING - MUTEX_T tracking_cs; - struct s_lane* volatile tracking_first; // will change to TRACKING_END if we want to activate tracking -#endif // HAVE_LANE_TRACKING - - MUTEX_T selfdestruct_cs; - - // require() serialization - MUTEX_T require_cs; - - // Lock for reference counter inc/dec locks (to be initialized by outside code) TODO: get rid of this and use atomics instead! - MUTEX_T deep_lock; - MUTEX_T mtid_lock; - - int last_mt_id; - -#if USE_DEBUG_SPEW - int debugspew_indent_depth; -#endif // USE_DEBUG_SPEW - - struct s_lane* volatile selfdestruct_first; - // After a lane has removed itself from the chain, it still performs some processing. - // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads - int volatile selfdestructing_count; }; -struct s_Universe* get_universe( lua_State* L); -extern void* const UNIVERSE_REGKEY; - // ################################################################################################ -#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*/ -#else - 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) \ - do \ - { \ - int a = lua_gettop( L) - _oldtop_##L; \ - int b = (change); \ - if( a != b) \ - luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", a, b, __FILE__, __LINE__ ); \ - } while( 0) - #define STACK_END(L,change) STACK_MID(L,change); } - - #define STACK_DUMP( L) luaG_dump( L) -#endif -#define ASSERT_L(c) _ASSERT_L(L,c) - -#define STACK_GROW( L, n) do { if (!lua_checkstack(L,(int)(n))) luaL_error( L, "Cannot grow stack!" ); } while( 0) - #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)) @@ -155,8 +45,8 @@ enum eLookupMode eLM_FromKeeper // send a function from a keeper state to a lane }; -char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_); -void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_); +char const* push_deep_proxy( struct s_Universe* U, lua_State* L, struct DEEP_PRELUDE* prelude, enum eLookupMode mode_); +void free_deep_prelude( lua_State* L, struct DEEP_PRELUDE* prelude_); int luaG_inter_copy_package( struct s_Universe* U, lua_State* L, lua_State* L2, int package_idx_, enum eLookupMode mode_); diff --git a/src/universe.c b/src/universe.c new file mode 100644 index 0000000..ba78396 --- /dev/null +++ b/src/universe.c @@ -0,0 +1,74 @@ +/* + * UNIVERSE.C Copyright (c) 2017, Benoit Germain + */ + +/* +=============================================================================== + +Copyright (C) 2017 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=============================================================================== +*/ + +#include "compat.h" +#include "macros_and_utils.h" +#include "universe.h" + +// crc64/we of string "UNIVERSE_REGKEY" generated at https://www.nitrxgen.net/hashgen/ +static void* const UNIVERSE_REGKEY = ((void*)0x9f877b2cf078f17f); + +// ################################################################################################ + +struct s_Universe* universe_create( lua_State* L) +{ + struct s_Universe* U = (struct s_Universe*) lua_newuserdata( L, sizeof(struct s_Universe)); // universe + memset( U, 0, sizeof( struct s_Universe)); + lua_pushlightuserdata( L, UNIVERSE_REGKEY); // universe UNIVERSE_REGKEY + lua_pushvalue( L, -2); // universe UNIVERSE_REGKEY universe + lua_rawset( L, LUA_REGISTRYINDEX); // universe + return U; +} + +// ################################################################################################ + +void universe_store( lua_State* L, struct s_Universe* U) +{ + STACK_CHECK( L); + lua_pushlightuserdata( L, UNIVERSE_REGKEY); + lua_pushlightuserdata( L, U); + lua_rawset( L, LUA_REGISTRYINDEX); + STACK_END( L, 0); +} + +// ################################################################################################ + +struct s_Universe* universe_get( lua_State* L) +{ + struct s_Universe* universe; + STACK_GROW( L, 2); + STACK_CHECK( L); + lua_pushlightuserdata( L, UNIVERSE_REGKEY); + lua_rawget( L, LUA_REGISTRYINDEX); + universe = lua_touserdata( L, -1); // NULL if nil + lua_pop( L, 1); + STACK_END( L, 0); + return universe; +} diff --git a/src/universe.h b/src/universe.h new file mode 100644 index 0000000..0ca5bf7 --- /dev/null +++ b/src/universe.h @@ -0,0 +1,66 @@ +/* +* UNIVERSE.H +*/ +#ifndef UNIVERSE_H +#define UNIVERSE_H + +#include "lua.h" +#include "threading.h" +// MUTEX_T + +// ################################################################################################ + +/* +* Do we want to activate full lane tracking feature? (EXPERIMENTAL) +*/ +#define HAVE_LANE_TRACKING 1 + +// ################################################################################################ + +// everything regarding the a Lanes universe is stored in that global structure +// held as a full userdata in the master Lua state that required it for the first time +// don't forget to initialize all members in LG_configure() +struct s_Universe +{ + // for verbose errors + bool_t verboseErrors; + + lua_CFunction on_state_create_func; + + struct s_Keepers* keepers; + + // Initialized by 'init_once_LOCKED()': the deep userdata Linda object + // used for timers (each lane will get a proxy to this) + volatile struct DEEP_PRELUDE* timer_deep; // = NULL + +#if HAVE_LANE_TRACKING + MUTEX_T tracking_cs; + struct s_lane* volatile tracking_first; // will change to TRACKING_END if we want to activate tracking +#endif // HAVE_LANE_TRACKING + + MUTEX_T selfdestruct_cs; + + // require() serialization + MUTEX_T require_cs; + + // Lock for reference counter inc/dec locks (to be initialized by outside code) TODO: get rid of this and use atomics instead! + MUTEX_T deep_lock; + MUTEX_T mtid_lock; + + int last_mt_id; + +#if USE_DEBUG_SPEW + int debugspew_indent_depth; +#endif // USE_DEBUG_SPEW + + struct s_lane* volatile selfdestruct_first; + // After a lane has removed itself from the chain, it still performs some processing. + // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads + int volatile selfdestructing_count; +}; + +struct s_Universe* universe_get( lua_State* L); +struct s_Universe* universe_create( lua_State* L); +void universe_store( lua_State* L, struct s_Universe* U); + +#endif // UNIVERSE_H -- cgit v1.2.3-55-g6feb