From d6f5a7795360e3c2a6fc2d424904b6daa1f2accd Mon Sep 17 00:00:00 2001 From: Benoit Germain <benoit.germain@ubisoft.com> Date: Tue, 16 Apr 2024 12:46:14 +0200 Subject: C++ migration: more conversion to InterCopyContext. debugspew indentation is managed by a scope object --- docs/index.html | 2 +- src/deep.h | 2 +- src/keeper.cpp | 59 ++++++++------- src/lanes.cpp | 61 ++++++++-------- src/macros_and_utils.h | 7 +- src/state.cpp | 44 ++++++------ src/tools.cpp | 192 +++++++++++++++++++++++++++---------------------- src/tools.h | 12 ++-- src/universe.h | 28 ++++++++ 9 files changed, 231 insertions(+), 176 deletions(-) diff --git a/docs/index.html b/docs/index.html index 67eccd5..9b3e5e7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1706,7 +1706,7 @@ static MyDeepFactory g_MyDeepFactory; Take a look at <tt>LindaFactory</tt> in <tt>linda.cpp</tt> or <tt>MyDeepFactory</tt> in <tt>deep_test.cpp</tt>. </li> <li>Include <tt>"deep.h"</tt> and either link against Lanes or statically compile <tt>compat.cpp deep.cpp tools.cpp universe.cpp</tt> into your module if you want to avoid a runtime dependency for users that will use your module without Lanes. - <li>Instanciate your userdata using <tt>yourFactoryObject.pushDeepUserdata()()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given a <tt>factory</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li> + <li>Instanciate your userdata using <tt>yourFactoryObject.pushDeepUserdata()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given a <tt>factory</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li> <li>Accessing the deep userdata from your C code, use <tt>yourFactoryObject.toDeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li> </ol> diff --git a/src/deep.h b/src/deep.h index 7c0aa6d..27aed8f 100644 --- a/src/deep.h +++ b/src/deep.h @@ -23,7 +23,7 @@ class Universe; enum class LookupMode { - LaneBody, // send the lane body directly from the source to the destination lane + LaneBody, // send the lane body directly from the source to the destination lane. keep this one first so that it's the value we get when we default-construct ToKeeper, // send a function from a lane to a keeper state FromKeeper // send a function from a keeper state to a lane }; diff --git a/src/keeper.cpp b/src/keeper.cpp index 650789b..3e26d5e 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -226,18 +226,19 @@ int keeper_push_linda_storage(Universe* U, Dest L, void* ptr_, uintptr_t magic_) return 0; } // move data from keeper to destination state - lua_pushnil(KL); // storage nil STACK_GROW(L, 5); STACK_CHECK_START_REL(L, 0); - lua_newtable(L); // out + lua_newtable(L); // out + InterCopyContext c{ U, L, KL, {}, {}, {}, LookupMode::FromKeeper, {} }; + lua_pushnil(KL); // storage nil while (lua_next(KL, -2)) // storage key fifo { keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl lua_pushvalue(KL, -2); // storage key fifotbl key - std::ignore = luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key fifotbl // out key + std::ignore = c.inter_move(1); // storage key fifotbl // out key STACK_CHECK(L, 2); lua_newtable(L); // out key keyout - std::ignore = luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key // out key keyout fifotbl + std::ignore = c.inter_move(1); // storage key // out key keyout fifotbl lua_pushinteger(L, fifo->first); // out key keyout fifotbl first STACK_CHECK(L, 5); lua_setfield(L, -3, "first"); // out key keyout fifotbl @@ -646,23 +647,24 @@ void close_keepers(Universe* U) * 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 + * settings table is expected at position 1 on the stack */ void init_keepers(Universe* U, lua_State* L) { + ASSERT_L(lua_gettop(L) == 1 && lua_istable(L, 1)); STACK_CHECK_START_REL(L, 0); // L K - lua_getfield(L, 1, "nb_keepers"); // nb_keepers + lua_getfield(L, 1, "nb_keepers"); // settings nb_keepers int const nb_keepers{ static_cast<int>(lua_tointeger(L, -1)) }; - lua_pop(L, 1); // + lua_pop(L, 1); // settings if (nb_keepers < 1) { luaL_error(L, "Bad number of keepers (%d)", nb_keepers); // doesn't return } STACK_CHECK(L, 0); - lua_getfield(L, 1, "keepers_gc_threshold"); // keepers_gc_threshold + lua_getfield(L, 1, "keepers_gc_threshold"); // settings keepers_gc_threshold int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L, -1)) }; - lua_pop(L, 1); // + lua_pop(L, 1); // settings STACK_CHECK(L, 0); // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states @@ -682,7 +684,7 @@ void init_keepers(Universe* U, lua_State* L) U->keepers->keeper_array[i].Keeper::Keeper(); } } - for (int i = 0; i < nb_keepers; ++i) // keepersUD + for (int i = 0; i < nb_keepers; ++i) // settings { // note that we will leak K if we raise an error later lua_State* const K{ create_state(U, L) }; @@ -706,26 +708,28 @@ void init_keepers(Universe* U, lua_State* L) // 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 - lua_pop(K, 1); // + luaL_requiref(K, "package", luaopen_package, 1); // settings package + lua_pop(K, 1); // settings STACK_CHECK(K, 0); serialize_require(DEBUGSPEW_PARAM_COMMA(U) K); STACK_CHECK(K, 0); - // copy package.path and package.cpath from the source state - lua_getglobal(L, "package"); // "..." keepersUD package + // copy package.path and package.cpath from the source state (TODO: use _R._LOADED.package instead of _G.package) + lua_getglobal(L, "package"); // settings package if (!lua_isnil(L, -1)) { // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately - if (luaG_inter_copy_package(U, Source{ L }, Dest{ K }, -1, LookupMode::ToKeeper) != InterCopyResult::Success) + InterCopyContext c{ U, Dest{ K }, Source{ L }, {}, SourceIndex{ lua_absindex(L, -1) }, {}, LookupMode::ToKeeper, {} }; + if (c.inter_copy_package() != InterCopyResult::Success) { // if something went wrong, the error message is at the top of the stack - lua_remove(L, -2); // error_msg + lua_remove(L, -2); // settings error_msg raise_lua_error(L); } } - lua_pop(L, 1); // + lua_pop(L, 1); // settings STACK_CHECK(L, 0); + STACK_CHECK(K, 0); // attempt to call on_state_create(), if we have one and it is a C function // (only support a C function because we can't transfer executable Lua code in keepers) @@ -837,20 +841,23 @@ KeeperCallResult keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_ ASSERT_L(lua_gettop(K) == 0); STACK_GROW(K, 2); - - PUSH_KEEPER_FUNC(K, func_); // func_ - - lua_pushlightuserdata(K, linda); // func_ linda - - if ((args == 0) || luaG_inter_copy(U, Source{ L }, Dest{ K }, args, LookupMode::ToKeeper) == InterCopyResult::Success) // func_ linda args... - { - lua_call(K, 1 + args, LUA_MULTRET); // result... + PUSH_KEEPER_FUNC(K, func_); // func_ + lua_pushlightuserdata(K, linda); // func_ linda + if ( + (args == 0) || + (InterCopyContext{ U, Dest{ K }, Source{ L }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success) + ) + { // func_ linda args... + lua_call(K, 1 + args, LUA_MULTRET); // result... int const retvals{ lua_gettop(K) - top_K }; // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired // this may interrupt a lane, causing the destruction of the underlying OS thread // after this, another lane making use of this keeper can get an error code from the mutex-locking function // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) - if ((retvals == 0) || (luaG_inter_move(U, Source{ K }, Dest{ L }, retvals, LookupMode::FromKeeper) == InterCopyResult::Success)) // K->L + if ( + (retvals == 0) || + (InterCopyContext{ U, Dest{ L }, Source{ K }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success) + ) // K->L { result.emplace(retvals); } diff --git a/src/lanes.cpp b/src/lanes.cpp index 8b4410a..17d4f67 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -946,13 +946,12 @@ LUAG_FUNC(require) DEBUGSPEW_CODE(Universe* U = universe_get(L)); STACK_CHECK_START_REL(L, 0); DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); lua_pushvalue(L, lua_upvalueindex(1)); // "name" require lua_insert(L, 1); // require "name" lua_call(L, nargs, 1); // module populate_func_lookup_table(L, -1, name); DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); STACK_CHECK(L, 0); return 1; } @@ -972,10 +971,9 @@ LUAG_FUNC(register) DEBUGSPEW_CODE(Universe* U = universe_get(L)); STACK_CHECK_START_REL(L, 0); // "name" mod_table DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); populate_func_lookup_table(L, -1, name); DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); STACK_CHECK(L, 0); return 0; } @@ -1022,7 +1020,6 @@ LUAG_FUNC(lane_new) /* --- Create and prepare the sub state --- */ DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); // populate with selected libraries at the same time lua_State* const L2{ luaG_newstate(U, Source{ L }, libs_str) }; // L // L2 @@ -1041,7 +1038,8 @@ LUAG_FUNC(lane_new) lua_State* const m_L; Lane* m_lane{ nullptr }; int const m_gc_cb_idx; - DEBUGSPEW_CODE(Universe* const U); // for DEBUGSPEW only (hence the absence of m_ prefix) + DEBUGSPEW_CODE(Universe* const U); + DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); public: @@ -1050,7 +1048,9 @@ LUAG_FUNC(lane_new) , m_lane{ lane_ } , m_gc_cb_idx{ gc_cb_idx_ } DEBUGSPEW_COMMA_PARAM(U{ U_ }) - {} + DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) + { + } ~OnExit() { @@ -1132,7 +1132,8 @@ LUAG_FUNC(lane_new) { DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack - [[maybe_unused]] InterCopyResult const ret{ luaG_inter_copy_package(U, Source{ L }, Dest{ L2 }, package_idx, LookupMode::LaneBody) }; + InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, SourceIndex{ package_idx }, {}, {}, {} }; + [[maybe_unused]] InterCopyResult const ret{ c.inter_copy_package() }; ASSERT_L(ret == InterCopyResult::Success); // either all went well, or we should not even get here } @@ -1141,7 +1142,7 @@ LUAG_FUNC(lane_new) { int nbRequired = 1; DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); // should not happen, was checked in lanes.lua before calling lane_new() if (lua_type(L, required_idx) != LUA_TTABLE) { @@ -1175,10 +1176,8 @@ LUAG_FUNC(lane_new) if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode { // propagate error to main state if any - std::ignore = luaG_inter_move(U - , Source{ L2 }, Dest{ L } - , 1, LookupMode::LaneBody - ); // func libs priority globals package required gc_cb [... args ...] n "modname" error + InterCopyContext c{ U, Dest{ L }, Source{ L2 }, {}, {}, {}, {}, {} }; + std::ignore = c.inter_move(1); // func libs priority globals package required gc_cb [... args ...] n "modname" error raise_lua_error(L); } // after requiring the module, register the functions it exported in our name<->function database @@ -1189,7 +1188,6 @@ LUAG_FUNC(lane_new) lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n ++ nbRequired; } // func libs priority globals package required gc_cb [... args ...] - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } STACK_CHECK(L, 0); STACK_CHECK(L2, 0); // @@ -1205,20 +1203,19 @@ LUAG_FUNC(lane_new) luaL_error(L, "Expected table, got %s", luaL_typename(L, globals_idx)); // doesn't return } - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil // Lua 5.2 wants us to push the globals table on the stack - lua_pushglobaltable(L2); // _G + InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, {}, {}, {}, {} }; + lua_pushglobaltable(L2); // _G while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v { - std::ignore = luaG_inter_copy(U, Source{ L }, Dest{ L2 }, 2, LookupMode::LaneBody); // _G k v + std::ignore = c.inter_copy(2); // _G k v // assign it in L2's globals table lua_rawset(L2, -3); // _G lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k } // func libs priority globals package required gc_cb [... args ...] lua_pop( L2, 1); // - - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } STACK_CHECK(L, 0); STACK_CHECK(L2, 0); @@ -1228,10 +1225,10 @@ LUAG_FUNC(lane_new) if (func_type == LuaType::FUNCTION) { DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func - InterCopyResult const res{ luaG_inter_move(U, Source{ L }, Dest{ L2 }, 1, LookupMode::LaneBody) }; // func libs priority globals package required gc_cb [... args ...] // func - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); + InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, {}, {}, {}, {} }; + InterCopyResult const res{ c.inter_move(1) }; // func libs priority globals package required gc_cb [... args ...] // func if (res != InterCopyResult::Success) { luaL_error(L, "tried to copy unsupported types"); // doesn't return @@ -1258,9 +1255,9 @@ LUAG_FUNC(lane_new) if (nargs > 0) { DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); - InterCopyResult const res{ luaG_inter_move(U, Source{ L }, Dest{ L2 }, nargs, LookupMode::LaneBody) }; // func libs priority globals package required gc_cb // func [... args ...] - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); + InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, {}, {}, {}, {} }; + InterCopyResult const res{ c.inter_move(nargs) }; // func libs priority globals package required gc_cb // func [... args ...] if (res != InterCopyResult::Success) { luaL_error(L, "tried to copy unsupported types"); // doesn't return @@ -1278,7 +1275,6 @@ LUAG_FUNC(lane_new) onExit.success(); // we should have the lane userdata on top of the stack STACK_CHECK(L, 1); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); return 1; } @@ -1423,7 +1419,10 @@ LUAG_FUNC(thread_join) case Lane::Done: { int const n{ lua_gettop(L2) }; // whole L2 stack - if ((n > 0) && (luaG_inter_move(U, Source{ L2 }, Dest{ L }, n, LookupMode::LaneBody) != InterCopyResult::Success)) + if ( + (n > 0) && + (InterCopyContext{ U, Dest{ L }, Source{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success) + ) { luaL_error(L, "tried to copy unsupported types"); // doesn't return } @@ -1437,7 +1436,8 @@ LUAG_FUNC(thread_join) STACK_GROW(L, 3); lua_pushnil(L); // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... - if (luaG_inter_move(U, Source{ L2 }, Dest{ L }, n, LookupMode::LaneBody) != InterCopyResult::Success) // nil "err" [trace] + InterCopyContext c{ U, Dest{ L }, Source{ L2 }, {}, {}, {}, {}, {} }; + if (c.inter_move(n) != InterCopyResult::Success) // nil "err" [trace] { luaL_error(L, "tried to copy unsupported types: %s", lua_tostring(L, -n)); // doesn't return } @@ -1772,12 +1772,12 @@ LUAG_FUNC(configure) STACK_CHECK_START_ABS(L, 1); // settings DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); - DEBUGSPEW_CODE(if (U) U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); if (U == nullptr) { U = universe_create(L); // settings universe - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope2{ U }); lua_newtable( L); // settings universe mt lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout lua_getfield(L, 1, "shutdown_mode"); // settings universe mt shutdown_timeout shutdown_mode @@ -1920,7 +1920,6 @@ LUAG_FUNC(configure) CONFIG_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); }); STACK_CHECK(L, 1); DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); // Return the settings table return 1; } diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h index 77bcfe2..073e940 100644 --- a/src/macros_and_utils.h +++ b/src/macros_and_utils.h @@ -19,9 +19,8 @@ using namespace std::chrono_literals; #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.load(std::memory_order_relaxed) : 0), debugspew_indent +#define INDENT_END , (U ? U->debugspew_indent_depth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent #define DEBUGSPEW_CODE(_code) _code #define DEBUGSPEW_OR_NOT(a_, b_) a_ #define DEBUGSPEW_PARAM_COMMA(param_) param_, @@ -114,8 +113,8 @@ class StackChecker } }; -#define STACK_CHECK_START_REL(L, offset_) StackChecker stackChecker_##L(L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__) -#define STACK_CHECK_START_ABS(L, offset_) StackChecker stackChecker_##L(L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__) +#define STACK_CHECK_START_REL(L, offset_) StackChecker stackChecker_##L{L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__} +#define STACK_CHECK_START_ABS(L, offset_) StackChecker stackChecker_##L{L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__} #define STACK_CHECK_RESET_REL(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__} #define STACK_CHECK_RESET_ABS(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__} #define STACK_CHECK(L, offset_) stackChecker_##L.check(offset_, __FILE__, __LINE__) diff --git a/src/state.cpp b/src/state.cpp index 4a5f995..1ba5a77 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -191,26 +191,27 @@ static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, char const // ################################################################################################# // just like lua_xmove, args are (from, to) -static void copy_one_time_settings(Universe* U, Source L, Dest L2) +static void copy_one_time_settings(Universe* U, Source L1, Dest L2) { - STACK_GROW(L, 2); - STACK_CHECK_START_REL(L, 0); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); + + STACK_GROW(L1, 2); + STACK_CHECK_START_REL(L1, 0); STACK_CHECK_START_REL(L2, 0); DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); - CONFIG_REGKEY.pushValue(L); // config + CONFIG_REGKEY.pushValue(L1); // config // copy settings from from source to destination registry - if (luaG_inter_move(U, L, L2, 1, LookupMode::LaneBody) != InterCopyResult::Success) // // config + InterCopyContext c{ U, L2, L1, {}, {}, {}, {}, {} }; + if (c.inter_move(1) != InterCopyResult::Success) // // config { - luaL_error( L, "failed to copy settings when loading lanes.core"); // doesn't return + luaL_error(L1, "failed to copy settings when loading lanes.core"); // doesn't return } // set L2:_R[CONFIG_REGKEY] = settings CONFIG_REGKEY.setValue(L2, [](lua_State* L) { lua_insert(L, -2); }); // config STACK_CHECK(L2, 0); - STACK_CHECK(L, 0); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); + STACK_CHECK(L1, 0); } // ################################################################################################# @@ -355,7 +356,7 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_) } DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); // copy settings (for example because it may contain a Lua on_state_create function) copy_one_time_settings( U, from_, L); @@ -426,24 +427,23 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_) STACK_CHECK(L, 1); populate_func_lookup_table(L, -1, nullptr); -#if 0 && USE_DEBUG_SPEW() +#if 1 && USE_DEBUG_SPEW() // dump the lookup database contents - lua_getfield(L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {} - lua_pushnil(L); // {} nil - while (lua_next(L, -2)) // {} k v + LOOKUP_REGKEY.pushValue(L); // {} + lua_pushnil(L); // {} nil + while (lua_next(L, -2)) // {} k v { - lua_getglobal(L, "print"); // {} k v print - lua_pushlstring(L, debugspew_indent, U->debugspew_indent_depth.load(std::memory_order_relaxed)); // {} k v print " " - lua_pushvalue(L, -4); // {} k v print " " k - lua_pushvalue(L, -4); // {} k v print " " k v - lua_call(L, 3, 0); // {} k v - lua_pop(L, 1); // {} k + lua_getglobal(L, "print"); // {} k v print + lua_pushlstring(L, DebugSpewIndentScope::debugspew_indent, U->debugspew_indent_depth.load(std::memory_order_relaxed)); // {} k v print " " + lua_pushvalue(L, -4); // {} k v print " " k + lua_pushvalue(L, -4); // {} k v print " " k v + lua_call(L, 3, 0); // {} k v + lua_pop(L, 1); // {} k } - lua_pop(L, 1); // {} + lua_pop(L, 1); // {} #endif // USE_DEBUG_SPEW() lua_pop(L, 1); STACK_CHECK(L, 0); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); return L; } diff --git a/src/tools.cpp b/src/tools.cpp index c995fdd..ad706be 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -38,7 +38,7 @@ THE SOFTWARE. // functions implemented in deep.c extern void push_registry_subtable( lua_State* L, UniqueKey key_); -DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); +DEBUGSPEW_CODE(char const* const DebugSpewIndentScope::debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); // ################################################################################################ @@ -348,7 +348,7 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L char const* prevName; DEBUGSPEW_CODE(char const *newName); DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); STACK_CHECK_START_REL(L, 0); // first, raise an error if the function is already known @@ -408,7 +408,6 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L } -- _depth; STACK_CHECK(L, -1); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } // ################################################################################################# @@ -423,7 +422,7 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) // 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(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); STACK_GROW( L, 6); // slot _i contains a table where we search for functions (or a full userdata with a metatable) @@ -445,7 +444,6 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) if( visit_count > 0) { DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); return; } @@ -500,7 +498,7 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) { DEBUGSPEW_CODE( char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); // un-visit this table in case we do need to process it lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} lua_rawget( L, cache); // ... {_i} {bfc} k {} n @@ -523,7 +521,6 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, lua_gettop( L), _depth); lua_pop( L, 1); // ... {_i} {bfc} k STACK_CHECK( L, 2); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } // remove table name from fqn stack lua_pushnil( L); // ... {_i} {bfc} nil @@ -533,7 +530,6 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_pop( L, 1); // ... {_i} STACK_CHECK( L, 0); // we are done // ... {_i} {bfc} - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } // ################################################################################################# @@ -548,8 +544,8 @@ void populate_func_lookup_table(lua_State* L, int i_, char const* name_) int start_depth = 0; DEBUGSPEW_CODE( Universe* U = universe_get( L)); DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "nullptr")); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); - STACK_GROW( L, 3); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); + STACK_GROW(L, 3); STACK_CHECK_START_REL(L, 0); LOOKUP_REGKEY.pushValue(L); // {} STACK_CHECK( L, 1); @@ -599,7 +595,6 @@ void populate_func_lookup_table(lua_State* L, int i_, char const* name_) (void) luaL_error( L, "unsupported module type %s", lua_typename( L, lua_type( L, in_base))); } STACK_CHECK( L, 0); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } // ################################################################################################# @@ -1782,9 +1777,8 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; else // regular function { DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", name)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); copy_cached_func(); // ... f - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); } STACK_CHECK(L2, 1); STACK_CHECK(L1, 0); @@ -1872,13 +1866,14 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; STACK_CHECK_START_REL(L1, 0); // L1 // L2 STACK_CHECK_START_REL(L2, 0); // L1 // L2 - DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); - DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[val_type], vt_names[static_cast<int>(vt)])); + DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); - // Non-POD can be skipped if its metatable contains { __lanesignore = true } LuaType val_type{ lua_type_as_enum(L1, L1_i) }; - if( ((1 << static_cast<int>(val_type)) & pod_mask) == 0) + DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)])); + + // Non-POD can be skipped if its metatable contains { __lanesignore = true } + if (((1 << static_cast<int>(val_type)) & pod_mask) == 0) { if (lua_getmetatable(L1, L1_i)) // ... mt { @@ -1974,8 +1969,6 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; break; } - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); - STACK_CHECK(L2, ret ? 1 : 0); STACK_CHECK(L1, 0); return ret; @@ -1983,30 +1976,25 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; // ################################################################################################# -/* -* Akin to 'lua_xmove' but copies values between _any_ Lua states. -* -* NOTE: Both the states must be solely in the current OS thread's posession. -* -* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. -*/ -[[nodiscard]] InterCopyResult luaG_inter_copy(Universe* U, Source L, Dest L2, int n, LookupMode mode_) +// Akin to 'lua_xmove' but copies values between _any_ Lua states. +// NOTE: Both the states must be solely in the current OS thread's possession. +[[nodiscard]] InterCopyResult InterCopyContext::inter_copy(int n_) const { - int const top_L{ lua_gettop(L) }; // ... {}n + _ASSERT_L(L1, vt == VT::NORMAL); - DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_inter_copy()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); + DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END)); + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); - if (n > top_L) + int const top_L1{ lua_gettop(L1) }; + if (n_ > top_L1) { // requesting to copy more than is available? DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); return InterCopyResult::NotEnoughValues; } STACK_CHECK_START_REL(L2, 0); - STACK_GROW(L2, n + 1); + STACK_GROW(L2, n_ + 1); /* * Make a cache table for the duration of this copy. Collects tables and @@ -2018,10 +2006,10 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; char tmpBuf[16]; char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" }; - InterCopyContext c{ U, L2, L, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode_, pBuf }; + InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf }; bool copyok{ true }; - STACK_CHECK_START_REL(L, 0); - for (int i = top_L - n + 1, j = 1; i <= top_L; ++i, ++j) + STACK_CHECK_START_REL(L1, 0); + for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) { if (U->verboseErrors) { @@ -2034,13 +2022,11 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; break; } } - STACK_CHECK(L, 0); - - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); + STACK_CHECK(L1, 0); if (copyok) { - STACK_CHECK(L2, n + 1); + STACK_CHECK(L2, n_ + 1); // 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. lua_remove(L2, top_L2 + 1); @@ -2055,74 +2041,108 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; // ################################################################################################# -[[nodiscard]] InterCopyResult luaG_inter_move(Universe* U, Source L, Dest L2, int n_, LookupMode mode_) +[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const { - InterCopyResult const ret{ luaG_inter_copy(U, L, L2, n_, mode_) }; - lua_pop( L, n_); + InterCopyResult const ret{ inter_copy(n_) }; + lua_pop( L1, n_); return ret; } // ################################################################################################# -// transfers stuff from L->_G["package"] to L2->_G["package"] +// transfers stuff from L1->_G["package"] to L2->_G["package"] // returns InterCopyResult::Success if everything is fine -// returns InterCopyResult::Error if pushed an error message in L -// else raise an error in L -[[nodiscard]] InterCopyResult luaG_inter_copy_package(Universe* U, Source L, Dest L2, int package_idx_, LookupMode mode_) +// returns InterCopyResult::Error if pushed an error message in L1 +// else raise an error in L1 +[[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const { - DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END)); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); - // package - STACK_CHECK_START_REL(L, 0); - STACK_CHECK_START_REL(L2, 0); - package_idx_ = lua_absindex(L, package_idx_); - if (lua_type(L, package_idx_) != LUA_TTABLE) + DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END)); + + class OnExit { - lua_pushfstring(L, "expected package as table, got %s", luaL_typename(L, package_idx_)); - STACK_CHECK(L, 1); + private: + + lua_State* const L2; + int const top_L2; + DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); + + public: + + OnExit(Universe* U_, lua_State* L2_) + : L2{ L2_ } + , top_L2{ lua_gettop(L2) } + DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) + { + } + + ~OnExit() + { + lua_settop(L2, top_L2); + } + } onExit{ U, L2 }; + + STACK_CHECK_START_REL(L1, 0); + if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) + { + lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i)); + STACK_CHECK(L1, 1); // raise the error when copying from lane to lane, else just leave it on the stack to be raised later - if (mode_ == LookupMode::LaneBody) + if (mode == LookupMode::LaneBody) { - lua_error(L); // doesn't return + lua_error(L1); // doesn't return } return InterCopyResult::Error; } - lua_getglobal(L2, "package"); - if (!lua_isnil(L2, -1)) // package library not loaded: do nothing + lua_getglobal(L2, "package"); // TODO: use _R._LOADED.package instead of _G.package + if (lua_isnil(L2, -1)) // package library not loaded: do nothing { - // package.loaders is renamed package.searchers in Lua 5.2 - // but don't copy it anyway, as the function names change depending on the slot index! - // users should provide an on_state_create function to setup custom loaders instead - // don't copy package.preload in keeper states (they don't know how to translate functions) - char const* entries[] = { "path", "cpath", (mode_ == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; - for (char const* const entry : entries) + DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END)); + STACK_CHECK(L1, 0); + return InterCopyResult::Success; + } + + InterCopyResult result{ InterCopyResult::Success }; + // package.loaders is renamed package.searchers in Lua 5.2 + // but don't copy it anyway, as the function names change depending on the slot index! + // users should provide an on_state_create function to setup custom loaders instead + // don't copy package.preload in keeper states (they don't know how to translate functions) + char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; + for (char const* const entry : entries) + { + if (!entry) + { + continue; + } + DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entry)); + lua_getfield(L1, L1_i, entry); + if (lua_isnil(L1, -1)) + { + lua_pop(L1, 1); + } + else { - if (!entry) { - continue; + DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); + result = inter_move(1); // moves the entry to L2 + STACK_CHECK(L1, 0); } - DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entry)); - lua_getfield(L, package_idx_, entry); - if (lua_isnil(L, -1)) + if (result == InterCopyResult::Success) { - lua_pop(L, 1); + lua_setfield(L2, -2, entry); // set package[entry] } else { - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); - std::ignore = luaG_inter_move(U, L, L2, 1, mode_); // moves the entry to L2 - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); - lua_setfield(L2, -2, entry); // set package[entry] + lua_pushfstring(L1, "failed to copy package entry %s", entry); + // raise the error when copying from lane to lane, else just leave it on the stack to be raised later + if (mode == LookupMode::LaneBody) + { + lua_error(L1); // doesn't return + } + lua_pop(L1, 1); + break; } } } - else - { - DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END)); - } - lua_pop(L2, 1); - STACK_CHECK(L2, 0); - STACK_CHECK(L, 0); - DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); - return InterCopyResult::Success; + STACK_CHECK(L1, 0); + return result; } diff --git a/src/tools.h b/src/tools.h index e14ac65..06646d3 100644 --- a/src/tools.h +++ b/src/tools.h @@ -19,7 +19,7 @@ void push_registry_subtable(lua_State* L, UniqueKey key_); enum class VT { - NORMAL, + NORMAL, // keep this one first so that it's the value we get when we default-construct KEY, METATABLE }; @@ -60,14 +60,16 @@ struct InterCopyContext void copy_func() const; void copy_cached_func() const; void inter_copy_keyvaluepair() const; + + public: + + [[nodiscard]] InterCopyResult inter_copy_package() const; + [[nodiscard]] InterCopyResult inter_copy(int n_) const; + [[nodiscard]] InterCopyResult inter_move(int n_) const; }; // ################################################################################################ -[[nodiscard]] InterCopyResult luaG_inter_copy_package(Universe* U, Source L, Dest L2, int package_idx_, LookupMode mode_); -[[nodiscard]] InterCopyResult luaG_inter_copy(Universe* U, Source L, Dest L2, int n, LookupMode mode_); -[[nodiscard]] InterCopyResult luaG_inter_move(Universe* U, Source L, Dest L2, int n, LookupMode mode_); - [[nodiscard]] int luaG_nameof(lua_State* L); void populate_func_lookup_table(lua_State* L, int _i, char const* _name); diff --git a/src/universe.h b/src/universe.h index 113ed21..eb85133 100644 --- a/src/universe.h +++ b/src/universe.h @@ -188,3 +188,31 @@ class Universe [[nodiscard]] Universe* universe_get(lua_State* L); [[nodiscard]] Universe* universe_create(lua_State* L); void universe_store(lua_State* L, Universe* U); + +// ################################################################################################ + +#if USE_DEBUG_SPEW() +class DebugSpewIndentScope +{ + private: + + Universe* const U; + + public: + + static char const* const debugspew_indent; + + DebugSpewIndentScope(Universe* U_) + : U{ U_ } + { + if (U) + U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed); + } + + ~DebugSpewIndentScope() + { + if (U) + U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed); + } +}; +#endif // USE_DEBUG_SPEW() -- cgit v1.2.3-55-g6feb