aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/index.html2
-rw-r--r--src/deep.h2
-rw-r--r--src/keeper.cpp59
-rw-r--r--src/lanes.cpp61
-rw-r--r--src/macros_and_utils.h7
-rw-r--r--src/state.cpp44
-rw-r--r--src/tools.cpp192
-rw-r--r--src/tools.h12
-rw-r--r--src/universe.h28
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;
1706 Take a look at <tt>LindaFactory</tt> in <tt>linda.cpp</tt> or <tt>MyDeepFactory</tt> in <tt>deep_test.cpp</tt>. 1706 Take a look at <tt>LindaFactory</tt> in <tt>linda.cpp</tt> or <tt>MyDeepFactory</tt> in <tt>deep_test.cpp</tt>.
1707 </li> 1707 </li>
1708 <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. 1708 <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.
1709 <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> 1709 <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>
1710 <li>Accessing the deep userdata from your C code, use <tt>yourFactoryObject.toDeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li> 1710 <li>Accessing the deep userdata from your C code, use <tt>yourFactoryObject.toDeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li>
1711</ol> 1711</ol>
1712 1712
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;
23 23
24enum class LookupMode 24enum class LookupMode
25{ 25{
26 LaneBody, // send the lane body directly from the source to the destination lane 26 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
27 ToKeeper, // send a function from a lane to a keeper state 27 ToKeeper, // send a function from a lane to a keeper state
28 FromKeeper // send a function from a keeper state to a lane 28 FromKeeper // send a function from a keeper state to a lane
29}; 29};
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_)
226 return 0; 226 return 0;
227 } 227 }
228 // move data from keeper to destination state 228 // move data from keeper to destination state
229 lua_pushnil(KL); // storage nil
230 STACK_GROW(L, 5); 229 STACK_GROW(L, 5);
231 STACK_CHECK_START_REL(L, 0); 230 STACK_CHECK_START_REL(L, 0);
232 lua_newtable(L); // out 231 lua_newtable(L); // out
232 InterCopyContext c{ U, L, KL, {}, {}, {}, LookupMode::FromKeeper, {} };
233 lua_pushnil(KL); // storage nil
233 while (lua_next(KL, -2)) // storage key fifo 234 while (lua_next(KL, -2)) // storage key fifo
234 { 235 {
235 keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl 236 keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl
236 lua_pushvalue(KL, -2); // storage key fifotbl key 237 lua_pushvalue(KL, -2); // storage key fifotbl key
237 std::ignore = luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key fifotbl // out key 238 std::ignore = c.inter_move(1); // storage key fifotbl // out key
238 STACK_CHECK(L, 2); 239 STACK_CHECK(L, 2);
239 lua_newtable(L); // out key keyout 240 lua_newtable(L); // out key keyout
240 std::ignore = luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key // out key keyout fifotbl 241 std::ignore = c.inter_move(1); // storage key // out key keyout fifotbl
241 lua_pushinteger(L, fifo->first); // out key keyout fifotbl first 242 lua_pushinteger(L, fifo->first); // out key keyout fifotbl first
242 STACK_CHECK(L, 5); 243 STACK_CHECK(L, 5);
243 lua_setfield(L, -3, "first"); // out key keyout fifotbl 244 lua_setfield(L, -3, "first"); // out key keyout fifotbl
@@ -646,23 +647,24 @@ void close_keepers(Universe* U)
646 * Note: Any problems would be design flaws; the created Lua state is left 647 * Note: Any problems would be design flaws; the created Lua state is left
647 * unclosed, because it does not really matter. In production code, this 648 * unclosed, because it does not really matter. In production code, this
648 * function never fails. 649 * function never fails.
649 * settings table is at position 1 on the stack 650 * settings table is expected at position 1 on the stack
650 */ 651 */
651void init_keepers(Universe* U, lua_State* L) 652void init_keepers(Universe* U, lua_State* L)
652{ 653{
654 ASSERT_L(lua_gettop(L) == 1 && lua_istable(L, 1));
653 STACK_CHECK_START_REL(L, 0); // L K 655 STACK_CHECK_START_REL(L, 0); // L K
654 lua_getfield(L, 1, "nb_keepers"); // nb_keepers 656 lua_getfield(L, 1, "nb_keepers"); // settings nb_keepers
655 int const nb_keepers{ static_cast<int>(lua_tointeger(L, -1)) }; 657 int const nb_keepers{ static_cast<int>(lua_tointeger(L, -1)) };
656 lua_pop(L, 1); // 658 lua_pop(L, 1); // settings
657 if (nb_keepers < 1) 659 if (nb_keepers < 1)
658 { 660 {
659 luaL_error(L, "Bad number of keepers (%d)", nb_keepers); // doesn't return 661 luaL_error(L, "Bad number of keepers (%d)", nb_keepers); // doesn't return
660 } 662 }
661 STACK_CHECK(L, 0); 663 STACK_CHECK(L, 0);
662 664
663 lua_getfield(L, 1, "keepers_gc_threshold"); // keepers_gc_threshold 665 lua_getfield(L, 1, "keepers_gc_threshold"); // settings keepers_gc_threshold
664 int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L, -1)) }; 666 int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L, -1)) };
665 lua_pop(L, 1); // 667 lua_pop(L, 1); // settings
666 STACK_CHECK(L, 0); 668 STACK_CHECK(L, 0);
667 669
668 // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states 670 // 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)
682 U->keepers->keeper_array[i].Keeper::Keeper(); 684 U->keepers->keeper_array[i].Keeper::Keeper();
683 } 685 }
684 } 686 }
685 for (int i = 0; i < nb_keepers; ++i) // keepersUD 687 for (int i = 0; i < nb_keepers; ++i) // settings
686 { 688 {
687 // note that we will leak K if we raise an error later 689 // note that we will leak K if we raise an error later
688 lua_State* const K{ create_state(U, L) }; 690 lua_State* const K{ create_state(U, L) };
@@ -706,26 +708,28 @@ void init_keepers(Universe* U, lua_State* L)
706 708
707 // make sure 'package' is initialized in keeper states, so that we have require() 709 // make sure 'package' is initialized in keeper states, so that we have require()
708 // this because this is needed when transferring deep userdata object 710 // this because this is needed when transferring deep userdata object
709 luaL_requiref(K, "package", luaopen_package, 1); // package 711 luaL_requiref(K, "package", luaopen_package, 1); // settings package
710 lua_pop(K, 1); // 712 lua_pop(K, 1); // settings
711 STACK_CHECK(K, 0); 713 STACK_CHECK(K, 0);
712 serialize_require(DEBUGSPEW_PARAM_COMMA(U) K); 714 serialize_require(DEBUGSPEW_PARAM_COMMA(U) K);
713 STACK_CHECK(K, 0); 715 STACK_CHECK(K, 0);
714 716
715 // copy package.path and package.cpath from the source state 717 // copy package.path and package.cpath from the source state (TODO: use _R._LOADED.package instead of _G.package)
716 lua_getglobal(L, "package"); // "..." keepersUD package 718 lua_getglobal(L, "package"); // settings package
717 if (!lua_isnil(L, -1)) 719 if (!lua_isnil(L, -1))
718 { 720 {
719 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately 721 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately
720 if (luaG_inter_copy_package(U, Source{ L }, Dest{ K }, -1, LookupMode::ToKeeper) != InterCopyResult::Success) 722 InterCopyContext c{ U, Dest{ K }, Source{ L }, {}, SourceIndex{ lua_absindex(L, -1) }, {}, LookupMode::ToKeeper, {} };
723 if (c.inter_copy_package() != InterCopyResult::Success)
721 { 724 {
722 // if something went wrong, the error message is at the top of the stack 725 // if something went wrong, the error message is at the top of the stack
723 lua_remove(L, -2); // error_msg 726 lua_remove(L, -2); // settings error_msg
724 raise_lua_error(L); 727 raise_lua_error(L);
725 } 728 }
726 } 729 }
727 lua_pop(L, 1); // 730 lua_pop(L, 1); // settings
728 STACK_CHECK(L, 0); 731 STACK_CHECK(L, 0);
732 STACK_CHECK(K, 0);
729 733
730 // attempt to call on_state_create(), if we have one and it is a C function 734 // attempt to call on_state_create(), if we have one and it is a C function
731 // (only support a C function because we can't transfer executable Lua code in keepers) 735 // (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_
837 ASSERT_L(lua_gettop(K) == 0); 841 ASSERT_L(lua_gettop(K) == 0);
838 842
839 STACK_GROW(K, 2); 843 STACK_GROW(K, 2);
840 844 PUSH_KEEPER_FUNC(K, func_); // func_
841 PUSH_KEEPER_FUNC(K, func_); // func_ 845 lua_pushlightuserdata(K, linda); // func_ linda
842 846 if (
843 lua_pushlightuserdata(K, linda); // func_ linda 847 (args == 0) ||
844 848 (InterCopyContext{ U, Dest{ K }, Source{ L }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success)
845 if ((args == 0) || luaG_inter_copy(U, Source{ L }, Dest{ K }, args, LookupMode::ToKeeper) == InterCopyResult::Success) // func_ linda args... 849 )
846 { 850 { // func_ linda args...
847 lua_call(K, 1 + args, LUA_MULTRET); // result... 851 lua_call(K, 1 + args, LUA_MULTRET); // result...
848 int const retvals{ lua_gettop(K) - top_K }; 852 int const retvals{ lua_gettop(K) - top_K };
849 // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired 853 // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired
850 // this may interrupt a lane, causing the destruction of the underlying OS thread 854 // this may interrupt a lane, causing the destruction of the underlying OS thread
851 // after this, another lane making use of this keeper can get an error code from the mutex-locking function 855 // after this, another lane making use of this keeper can get an error code from the mutex-locking function
852 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) 856 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread)
853 if ((retvals == 0) || (luaG_inter_move(U, Source{ K }, Dest{ L }, retvals, LookupMode::FromKeeper) == InterCopyResult::Success)) // K->L 857 if (
858 (retvals == 0) ||
859 (InterCopyContext{ U, Dest{ L }, Source{ K }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success)
860 ) // K->L
854 { 861 {
855 result.emplace(retvals); 862 result.emplace(retvals);
856 } 863 }
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)
946 DEBUGSPEW_CODE(Universe* U = universe_get(L)); 946 DEBUGSPEW_CODE(Universe* U = universe_get(L));
947 STACK_CHECK_START_REL(L, 0); 947 STACK_CHECK_START_REL(L, 0);
948 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); 948 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name));
949 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 949 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
950 lua_pushvalue(L, lua_upvalueindex(1)); // "name" require 950 lua_pushvalue(L, lua_upvalueindex(1)); // "name" require
951 lua_insert(L, 1); // require "name" 951 lua_insert(L, 1); // require "name"
952 lua_call(L, nargs, 1); // module 952 lua_call(L, nargs, 1); // module
953 populate_func_lookup_table(L, -1, name); 953 populate_func_lookup_table(L, -1, name);
954 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); 954 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name));
955 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
956 STACK_CHECK(L, 0); 955 STACK_CHECK(L, 0);
957 return 1; 956 return 1;
958} 957}
@@ -972,10 +971,9 @@ LUAG_FUNC(register)
972 DEBUGSPEW_CODE(Universe* U = universe_get(L)); 971 DEBUGSPEW_CODE(Universe* U = universe_get(L));
973 STACK_CHECK_START_REL(L, 0); // "name" mod_table 972 STACK_CHECK_START_REL(L, 0); // "name" mod_table
974 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); 973 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name));
975 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 974 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
976 populate_func_lookup_table(L, -1, name); 975 populate_func_lookup_table(L, -1, name);
977 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name)); 976 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name));
978 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
979 STACK_CHECK(L, 0); 977 STACK_CHECK(L, 0);
980 return 0; 978 return 0;
981} 979}
@@ -1022,7 +1020,6 @@ LUAG_FUNC(lane_new)
1022 1020
1023 /* --- Create and prepare the sub state --- */ 1021 /* --- Create and prepare the sub state --- */
1024 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END)); 1022 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END));
1025 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1026 1023
1027 // populate with selected libraries at the same time 1024 // populate with selected libraries at the same time
1028 lua_State* const L2{ luaG_newstate(U, Source{ L }, libs_str) }; // L // L2 1025 lua_State* const L2{ luaG_newstate(U, Source{ L }, libs_str) }; // L // L2
@@ -1041,7 +1038,8 @@ LUAG_FUNC(lane_new)
1041 lua_State* const m_L; 1038 lua_State* const m_L;
1042 Lane* m_lane{ nullptr }; 1039 Lane* m_lane{ nullptr };
1043 int const m_gc_cb_idx; 1040 int const m_gc_cb_idx;
1044 DEBUGSPEW_CODE(Universe* const U); // for DEBUGSPEW only (hence the absence of m_ prefix) 1041 DEBUGSPEW_CODE(Universe* const U);
1042 DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
1045 1043
1046 public: 1044 public:
1047 1045
@@ -1050,7 +1048,9 @@ LUAG_FUNC(lane_new)
1050 , m_lane{ lane_ } 1048 , m_lane{ lane_ }
1051 , m_gc_cb_idx{ gc_cb_idx_ } 1049 , m_gc_cb_idx{ gc_cb_idx_ }
1052 DEBUGSPEW_COMMA_PARAM(U{ U_ }) 1050 DEBUGSPEW_COMMA_PARAM(U{ U_ })
1053 {} 1051 DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
1052 {
1053 }
1054 1054
1055 ~OnExit() 1055 ~OnExit()
1056 { 1056 {
@@ -1132,7 +1132,8 @@ LUAG_FUNC(lane_new)
1132 { 1132 {
1133 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); 1133 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END));
1134 // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack 1134 // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack
1135 [[maybe_unused]] InterCopyResult const ret{ luaG_inter_copy_package(U, Source{ L }, Dest{ L2 }, package_idx, LookupMode::LaneBody) }; 1135 InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, SourceIndex{ package_idx }, {}, {}, {} };
1136 [[maybe_unused]] InterCopyResult const ret{ c.inter_copy_package() };
1136 ASSERT_L(ret == InterCopyResult::Success); // either all went well, or we should not even get here 1137 ASSERT_L(ret == InterCopyResult::Success); // either all went well, or we should not even get here
1137 } 1138 }
1138 1139
@@ -1141,7 +1142,7 @@ LUAG_FUNC(lane_new)
1141 { 1142 {
1142 int nbRequired = 1; 1143 int nbRequired = 1;
1143 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); 1144 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END));
1144 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1145 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1145 // should not happen, was checked in lanes.lua before calling lane_new() 1146 // should not happen, was checked in lanes.lua before calling lane_new()
1146 if (lua_type(L, required_idx) != LUA_TTABLE) 1147 if (lua_type(L, required_idx) != LUA_TTABLE)
1147 { 1148 {
@@ -1175,10 +1176,8 @@ LUAG_FUNC(lane_new)
1175 if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode 1176 if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode
1176 { 1177 {
1177 // propagate error to main state if any 1178 // propagate error to main state if any
1178 std::ignore = luaG_inter_move(U 1179 InterCopyContext c{ U, Dest{ L }, Source{ L2 }, {}, {}, {}, {}, {} };
1179 , Source{ L2 }, Dest{ L } 1180 std::ignore = c.inter_move(1); // func libs priority globals package required gc_cb [... args ...] n "modname" error
1180 , 1, LookupMode::LaneBody
1181 ); // func libs priority globals package required gc_cb [... args ...] n "modname" error
1182 raise_lua_error(L); 1181 raise_lua_error(L);
1183 } 1182 }
1184 // after requiring the module, register the functions it exported in our name<->function database 1183 // after requiring the module, register the functions it exported in our name<->function database
@@ -1189,7 +1188,6 @@ LUAG_FUNC(lane_new)
1189 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n 1188 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n
1190 ++ nbRequired; 1189 ++ nbRequired;
1191 } // func libs priority globals package required gc_cb [... args ...] 1190 } // func libs priority globals package required gc_cb [... args ...]
1192 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1193 } 1191 }
1194 STACK_CHECK(L, 0); 1192 STACK_CHECK(L, 0);
1195 STACK_CHECK(L2, 0); // 1193 STACK_CHECK(L2, 0); //
@@ -1205,20 +1203,19 @@ LUAG_FUNC(lane_new)
1205 luaL_error(L, "Expected table, got %s", luaL_typename(L, globals_idx)); // doesn't return 1203 luaL_error(L, "Expected table, got %s", luaL_typename(L, globals_idx)); // doesn't return
1206 } 1204 }
1207 1205
1208 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1206 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1209 lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil 1207 lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil
1210 // Lua 5.2 wants us to push the globals table on the stack 1208 // Lua 5.2 wants us to push the globals table on the stack
1211 lua_pushglobaltable(L2); // _G 1209 InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, {}, {}, {}, {} };
1210 lua_pushglobaltable(L2); // _G
1212 while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v 1211 while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v
1213 { 1212 {
1214 std::ignore = luaG_inter_copy(U, Source{ L }, Dest{ L2 }, 2, LookupMode::LaneBody); // _G k v 1213 std::ignore = c.inter_copy(2); // _G k v
1215 // assign it in L2's globals table 1214 // assign it in L2's globals table
1216 lua_rawset(L2, -3); // _G 1215 lua_rawset(L2, -3); // _G
1217 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k 1216 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k
1218 } // func libs priority globals package required gc_cb [... args ...] 1217 } // func libs priority globals package required gc_cb [... args ...]
1219 lua_pop( L2, 1); // 1218 lua_pop( L2, 1); //
1220
1221 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1222 } 1219 }
1223 STACK_CHECK(L, 0); 1220 STACK_CHECK(L, 0);
1224 STACK_CHECK(L2, 0); 1221 STACK_CHECK(L2, 0);
@@ -1228,10 +1225,10 @@ LUAG_FUNC(lane_new)
1228 if (func_type == LuaType::FUNCTION) 1225 if (func_type == LuaType::FUNCTION)
1229 { 1226 {
1230 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1227 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1231 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1228 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1232 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func 1229 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func
1233 InterCopyResult const res{ luaG_inter_move(U, Source{ L }, Dest{ L2 }, 1, LookupMode::LaneBody) }; // func libs priority globals package required gc_cb [... args ...] // func 1230 InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, {}, {}, {}, {} };
1234 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); 1231 InterCopyResult const res{ c.inter_move(1) }; // func libs priority globals package required gc_cb [... args ...] // func
1235 if (res != InterCopyResult::Success) 1232 if (res != InterCopyResult::Success)
1236 { 1233 {
1237 luaL_error(L, "tried to copy unsupported types"); // doesn't return 1234 luaL_error(L, "tried to copy unsupported types"); // doesn't return
@@ -1258,9 +1255,9 @@ LUAG_FUNC(lane_new)
1258 if (nargs > 0) 1255 if (nargs > 0)
1259 { 1256 {
1260 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1257 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1261 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1258 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1262 InterCopyResult const res{ luaG_inter_move(U, Source{ L }, Dest{ L2 }, nargs, LookupMode::LaneBody) }; // func libs priority globals package required gc_cb // func [... args ...] 1259 InterCopyContext c{ U, Dest{ L2 }, Source{ L }, {}, {}, {}, {}, {} };
1263 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); 1260 InterCopyResult const res{ c.inter_move(nargs) }; // func libs priority globals package required gc_cb // func [... args ...]
1264 if (res != InterCopyResult::Success) 1261 if (res != InterCopyResult::Success)
1265 { 1262 {
1266 luaL_error(L, "tried to copy unsupported types"); // doesn't return 1263 luaL_error(L, "tried to copy unsupported types"); // doesn't return
@@ -1278,7 +1275,6 @@ LUAG_FUNC(lane_new)
1278 onExit.success(); 1275 onExit.success();
1279 // we should have the lane userdata on top of the stack 1276 // we should have the lane userdata on top of the stack
1280 STACK_CHECK(L, 1); 1277 STACK_CHECK(L, 1);
1281 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1282 return 1; 1278 return 1;
1283} 1279}
1284 1280
@@ -1423,7 +1419,10 @@ LUAG_FUNC(thread_join)
1423 case Lane::Done: 1419 case Lane::Done:
1424 { 1420 {
1425 int const n{ lua_gettop(L2) }; // whole L2 stack 1421 int const n{ lua_gettop(L2) }; // whole L2 stack
1426 if ((n > 0) && (luaG_inter_move(U, Source{ L2 }, Dest{ L }, n, LookupMode::LaneBody) != InterCopyResult::Success)) 1422 if (
1423 (n > 0) &&
1424 (InterCopyContext{ U, Dest{ L }, Source{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success)
1425 )
1427 { 1426 {
1428 luaL_error(L, "tried to copy unsupported types"); // doesn't return 1427 luaL_error(L, "tried to copy unsupported types"); // doesn't return
1429 } 1428 }
@@ -1437,7 +1436,8 @@ LUAG_FUNC(thread_join)
1437 STACK_GROW(L, 3); 1436 STACK_GROW(L, 3);
1438 lua_pushnil(L); 1437 lua_pushnil(L);
1439 // 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 ... 1438 // 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 ...
1440 if (luaG_inter_move(U, Source{ L2 }, Dest{ L }, n, LookupMode::LaneBody) != InterCopyResult::Success) // nil "err" [trace] 1439 InterCopyContext c{ U, Dest{ L }, Source{ L2 }, {}, {}, {}, {}, {} };
1440 if (c.inter_move(n) != InterCopyResult::Success) // nil "err" [trace]
1441 { 1441 {
1442 luaL_error(L, "tried to copy unsupported types: %s", lua_tostring(L, -n)); // doesn't return 1442 luaL_error(L, "tried to copy unsupported types: %s", lua_tostring(L, -n)); // doesn't return
1443 } 1443 }
@@ -1772,12 +1772,12 @@ LUAG_FUNC(configure)
1772 STACK_CHECK_START_ABS(L, 1); // settings 1772 STACK_CHECK_START_ABS(L, 1); // settings
1773 1773
1774 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); 1774 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L));
1775 DEBUGSPEW_CODE(if (U) U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1775 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1776 1776
1777 if (U == nullptr) 1777 if (U == nullptr)
1778 { 1778 {
1779 U = universe_create(L); // settings universe 1779 U = universe_create(L); // settings universe
1780 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1780 DEBUGSPEW_CODE(DebugSpewIndentScope scope2{ U });
1781 lua_newtable( L); // settings universe mt 1781 lua_newtable( L); // settings universe mt
1782 lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout 1782 lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1783 lua_getfield(L, 1, "shutdown_mode"); // settings universe mt shutdown_timeout shutdown_mode 1783 lua_getfield(L, 1, "shutdown_mode"); // settings universe mt shutdown_timeout shutdown_mode
@@ -1920,7 +1920,6 @@ LUAG_FUNC(configure)
1920 CONFIG_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); }); 1920 CONFIG_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
1921 STACK_CHECK(L, 1); 1921 STACK_CHECK(L, 1);
1922 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L)); 1922 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L));
1923 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1924 // Return the settings table 1923 // Return the settings table
1925 return 1; 1924 return 1;
1926} 1925}
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;
19 19
20#define USE_DEBUG_SPEW() 0 20#define USE_DEBUG_SPEW() 0
21#if USE_DEBUG_SPEW() 21#if USE_DEBUG_SPEW()
22extern char const* debugspew_indent;
23#define INDENT_BEGIN "%.*s " 22#define INDENT_BEGIN "%.*s "
24#define INDENT_END , (U ? U->debugspew_indent_depth.load(std::memory_order_relaxed) : 0), debugspew_indent 23#define INDENT_END , (U ? U->debugspew_indent_depth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent
25#define DEBUGSPEW_CODE(_code) _code 24#define DEBUGSPEW_CODE(_code) _code
26#define DEBUGSPEW_OR_NOT(a_, b_) a_ 25#define DEBUGSPEW_OR_NOT(a_, b_) a_
27#define DEBUGSPEW_PARAM_COMMA(param_) param_, 26#define DEBUGSPEW_PARAM_COMMA(param_) param_,
@@ -114,8 +113,8 @@ class StackChecker
114 } 113 }
115}; 114};
116 115
117#define STACK_CHECK_START_REL(L, offset_) StackChecker stackChecker_##L(L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__) 116#define STACK_CHECK_START_REL(L, offset_) StackChecker stackChecker_##L{L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__}
118#define STACK_CHECK_START_ABS(L, offset_) StackChecker stackChecker_##L(L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__) 117#define STACK_CHECK_START_ABS(L, offset_) StackChecker stackChecker_##L{L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__}
119#define STACK_CHECK_RESET_REL(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__} 118#define STACK_CHECK_RESET_REL(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__}
120#define STACK_CHECK_RESET_ABS(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__} 119#define STACK_CHECK_RESET_ABS(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__}
121#define STACK_CHECK(L, offset_) stackChecker_##L.check(offset_, __FILE__, __LINE__) 120#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
191// ################################################################################################# 191// #################################################################################################
192 192
193// just like lua_xmove, args are (from, to) 193// just like lua_xmove, args are (from, to)
194static void copy_one_time_settings(Universe* U, Source L, Dest L2) 194static void copy_one_time_settings(Universe* U, Source L1, Dest L2)
195{ 195{
196 STACK_GROW(L, 2); 196 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
197 STACK_CHECK_START_REL(L, 0); 197
198 STACK_GROW(L1, 2);
199 STACK_CHECK_START_REL(L1, 0);
198 STACK_CHECK_START_REL(L2, 0); 200 STACK_CHECK_START_REL(L2, 0);
199 201
200 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END)); 202 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END));
201 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
202 203
203 CONFIG_REGKEY.pushValue(L); // config 204 CONFIG_REGKEY.pushValue(L1); // config
204 // copy settings from from source to destination registry 205 // copy settings from from source to destination registry
205 if (luaG_inter_move(U, L, L2, 1, LookupMode::LaneBody) != InterCopyResult::Success) // // config 206 InterCopyContext c{ U, L2, L1, {}, {}, {}, {}, {} };
207 if (c.inter_move(1) != InterCopyResult::Success) // // config
206 { 208 {
207 luaL_error( L, "failed to copy settings when loading lanes.core"); // doesn't return 209 luaL_error(L1, "failed to copy settings when loading lanes.core"); // doesn't return
208 } 210 }
209 // set L2:_R[CONFIG_REGKEY] = settings 211 // set L2:_R[CONFIG_REGKEY] = settings
210 CONFIG_REGKEY.setValue(L2, [](lua_State* L) { lua_insert(L, -2); }); // config 212 CONFIG_REGKEY.setValue(L2, [](lua_State* L) { lua_insert(L, -2); }); // config
211 STACK_CHECK(L2, 0); 213 STACK_CHECK(L2, 0);
212 STACK_CHECK(L, 0); 214 STACK_CHECK(L1, 0);
213 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
214} 215}
215 216
216// ################################################################################################# 217// #################################################################################################
@@ -355,7 +356,7 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_)
355 } 356 }
356 357
357 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END)); 358 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END));
358 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 359 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
359 360
360 // copy settings (for example because it may contain a Lua on_state_create function) 361 // copy settings (for example because it may contain a Lua on_state_create function)
361 copy_one_time_settings( U, from_, L); 362 copy_one_time_settings( U, from_, L);
@@ -426,24 +427,23 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_)
426 STACK_CHECK(L, 1); 427 STACK_CHECK(L, 1);
427 populate_func_lookup_table(L, -1, nullptr); 428 populate_func_lookup_table(L, -1, nullptr);
428 429
429#if 0 && USE_DEBUG_SPEW() 430#if 1 && USE_DEBUG_SPEW()
430 // dump the lookup database contents 431 // dump the lookup database contents
431 lua_getfield(L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {} 432 LOOKUP_REGKEY.pushValue(L); // {}
432 lua_pushnil(L); // {} nil 433 lua_pushnil(L); // {} nil
433 while (lua_next(L, -2)) // {} k v 434 while (lua_next(L, -2)) // {} k v
434 { 435 {
435 lua_getglobal(L, "print"); // {} k v print 436 lua_getglobal(L, "print"); // {} k v print
436 lua_pushlstring(L, debugspew_indent, U->debugspew_indent_depth.load(std::memory_order_relaxed)); // {} k v print " " 437 lua_pushlstring(L, DebugSpewIndentScope::debugspew_indent, U->debugspew_indent_depth.load(std::memory_order_relaxed)); // {} k v print " "
437 lua_pushvalue(L, -4); // {} k v print " " k 438 lua_pushvalue(L, -4); // {} k v print " " k
438 lua_pushvalue(L, -4); // {} k v print " " k v 439 lua_pushvalue(L, -4); // {} k v print " " k v
439 lua_call(L, 3, 0); // {} k v 440 lua_call(L, 3, 0); // {} k v
440 lua_pop(L, 1); // {} k 441 lua_pop(L, 1); // {} k
441 } 442 }
442 lua_pop(L, 1); // {} 443 lua_pop(L, 1); // {}
443#endif // USE_DEBUG_SPEW() 444#endif // USE_DEBUG_SPEW()
444 445
445 lua_pop(L, 1); 446 lua_pop(L, 1);
446 STACK_CHECK(L, 0); 447 STACK_CHECK(L, 0);
447 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
448 return L; 448 return L;
449} 449}
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.
38// functions implemented in deep.c 38// functions implemented in deep.c
39extern void push_registry_subtable( lua_State* L, UniqueKey key_); 39extern void push_registry_subtable( lua_State* L, UniqueKey key_);
40 40
41DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); 41DEBUGSPEW_CODE(char const* const DebugSpewIndentScope::debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
42 42
43 43
44// ################################################################################################ 44// ################################################################################################
@@ -348,7 +348,7 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L
348 char const* prevName; 348 char const* prevName;
349 DEBUGSPEW_CODE(char const *newName); 349 DEBUGSPEW_CODE(char const *newName);
350 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END)); 350 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END));
351 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 351 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
352 352
353 STACK_CHECK_START_REL(L, 0); 353 STACK_CHECK_START_REL(L, 0);
354 // first, raise an error if the function is already known 354 // 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
408 } 408 }
409 -- _depth; 409 -- _depth;
410 STACK_CHECK(L, -1); 410 STACK_CHECK(L, -1);
411 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
412} 411}
413 412
414// ################################################################################################# 413// #################################################################################################
@@ -423,7 +422,7 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U)
423 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) 422 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search)
424 int const breadth_first_cache = lua_gettop( L) + 1; 423 int const breadth_first_cache = lua_gettop( L) + 1;
425 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END)); 424 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END));
426 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 425 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
427 426
428 STACK_GROW( L, 6); 427 STACK_GROW( L, 6);
429 // slot _i contains a table where we search for functions (or a full userdata with a metatable) 428 // 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)
445 if( visit_count > 0) 444 if( visit_count > 0)
446 { 445 {
447 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END)); 446 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END));
448 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
449 return; 447 return;
450 } 448 }
451 449
@@ -500,7 +498,7 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U)
500 { 498 {
501 DEBUGSPEW_CODE( char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"); 499 DEBUGSPEW_CODE( char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string");
502 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key)); 500 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key));
503 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 501 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
504 // un-visit this table in case we do need to process it 502 // un-visit this table in case we do need to process it
505 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 503 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
506 lua_rawget( L, cache); // ... {_i} {bfc} k {} n 504 lua_rawget( L, cache); // ... {_i} {bfc} k {} n
@@ -523,7 +521,6 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U)
523 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, lua_gettop( L), _depth); 521 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, lua_gettop( L), _depth);
524 lua_pop( L, 1); // ... {_i} {bfc} k 522 lua_pop( L, 1); // ... {_i} {bfc} k
525 STACK_CHECK( L, 2); 523 STACK_CHECK( L, 2);
526 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
527 } 524 }
528 // remove table name from fqn stack 525 // remove table name from fqn stack
529 lua_pushnil( L); // ... {_i} {bfc} nil 526 lua_pushnil( L); // ... {_i} {bfc} nil
@@ -533,7 +530,6 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U)
533 lua_pop( L, 1); // ... {_i} 530 lua_pop( L, 1); // ... {_i}
534 STACK_CHECK( L, 0); 531 STACK_CHECK( L, 0);
535 // we are done // ... {_i} {bfc} 532 // we are done // ... {_i} {bfc}
536 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
537} 533}
538 534
539// ################################################################################################# 535// #################################################################################################
@@ -548,8 +544,8 @@ void populate_func_lookup_table(lua_State* L, int i_, char const* name_)
548 int start_depth = 0; 544 int start_depth = 0;
549 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 545 DEBUGSPEW_CODE( Universe* U = universe_get( L));
550 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "nullptr")); 546 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "nullptr"));
551 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 547 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
552 STACK_GROW( L, 3); 548 STACK_GROW(L, 3);
553 STACK_CHECK_START_REL(L, 0); 549 STACK_CHECK_START_REL(L, 0);
554 LOOKUP_REGKEY.pushValue(L); // {} 550 LOOKUP_REGKEY.pushValue(L); // {}
555 STACK_CHECK( L, 1); 551 STACK_CHECK( L, 1);
@@ -599,7 +595,6 @@ void populate_func_lookup_table(lua_State* L, int i_, char const* name_)
599 (void) luaL_error( L, "unsupported module type %s", lua_typename( L, lua_type( L, in_base))); 595 (void) luaL_error( L, "unsupported module type %s", lua_typename( L, lua_type( L, in_base)));
600 } 596 }
601 STACK_CHECK( L, 0); 597 STACK_CHECK( L, 0);
602 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
603} 598}
604 599
605// ################################################################################################# 600// #################################################################################################
@@ -1782,9 +1777,8 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1782 else // regular function 1777 else // regular function
1783 { 1778 {
1784 DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", name)); 1779 DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", name));
1785 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1780 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1786 copy_cached_func(); // ... f 1781 copy_cached_func(); // ... f
1787 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1788 } 1782 }
1789 STACK_CHECK(L2, 1); 1783 STACK_CHECK(L2, 1);
1790 STACK_CHECK(L1, 0); 1784 STACK_CHECK(L1, 0);
@@ -1872,13 +1866,14 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1872 STACK_CHECK_START_REL(L1, 0); // L1 // L2 1866 STACK_CHECK_START_REL(L1, 0); // L1 // L2
1873 STACK_CHECK_START_REL(L2, 0); // L1 // L2 1867 STACK_CHECK_START_REL(L2, 0); // L1 // L2
1874 1868
1875 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); 1869 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END));
1876 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1870 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1877 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[val_type], vt_names[static_cast<int>(vt)]));
1878 1871
1879 // Non-POD can be skipped if its metatable contains { __lanesignore = true }
1880 LuaType val_type{ lua_type_as_enum(L1, L1_i) }; 1872 LuaType val_type{ lua_type_as_enum(L1, L1_i) };
1881 if( ((1 << static_cast<int>(val_type)) & pod_mask) == 0) 1873 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)]));
1874
1875 // Non-POD can be skipped if its metatable contains { __lanesignore = true }
1876 if (((1 << static_cast<int>(val_type)) & pod_mask) == 0)
1882 { 1877 {
1883 if (lua_getmetatable(L1, L1_i)) // ... mt 1878 if (lua_getmetatable(L1, L1_i)) // ... mt
1884 { 1879 {
@@ -1974,8 +1969,6 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1974 break; 1969 break;
1975 } 1970 }
1976 1971
1977 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1978
1979 STACK_CHECK(L2, ret ? 1 : 0); 1972 STACK_CHECK(L2, ret ? 1 : 0);
1980 STACK_CHECK(L1, 0); 1973 STACK_CHECK(L1, 0);
1981 return ret; 1974 return ret;
@@ -1983,30 +1976,25 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1983 1976
1984// ################################################################################################# 1977// #################################################################################################
1985 1978
1986/* 1979// Akin to 'lua_xmove' but copies values between _any_ Lua states.
1987* Akin to 'lua_xmove' but copies values between _any_ Lua states. 1980// NOTE: Both the states must be solely in the current OS thread's possession.
1988* 1981[[nodiscard]] InterCopyResult InterCopyContext::inter_copy(int n_) const
1989* NOTE: Both the states must be solely in the current OS thread's posession.
1990*
1991* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'.
1992*/
1993[[nodiscard]] InterCopyResult luaG_inter_copy(Universe* U, Source L, Dest L2, int n, LookupMode mode_)
1994{ 1982{
1995 int const top_L{ lua_gettop(L) }; // ... {}n 1983 _ASSERT_L(L1, vt == VT::NORMAL);
1996 1984
1997 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_inter_copy()\n" INDENT_END)); 1985 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END));
1998 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1986 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1999 1987
2000 if (n > top_L) 1988 int const top_L1{ lua_gettop(L1) };
1989 if (n_ > top_L1)
2001 { 1990 {
2002 // requesting to copy more than is available? 1991 // requesting to copy more than is available?
2003 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); 1992 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END));
2004 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
2005 return InterCopyResult::NotEnoughValues; 1993 return InterCopyResult::NotEnoughValues;
2006 } 1994 }
2007 1995
2008 STACK_CHECK_START_REL(L2, 0); 1996 STACK_CHECK_START_REL(L2, 0);
2009 STACK_GROW(L2, n + 1); 1997 STACK_GROW(L2, n_ + 1);
2010 1998
2011 /* 1999 /*
2012 * Make a cache table for the duration of this copy. Collects tables and 2000 * Make a cache table for the duration of this copy. Collects tables and
@@ -2018,10 +2006,10 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
2018 2006
2019 char tmpBuf[16]; 2007 char tmpBuf[16];
2020 char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" }; 2008 char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
2021 InterCopyContext c{ U, L2, L, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode_, pBuf }; 2009 InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf };
2022 bool copyok{ true }; 2010 bool copyok{ true };
2023 STACK_CHECK_START_REL(L, 0); 2011 STACK_CHECK_START_REL(L1, 0);
2024 for (int i = top_L - n + 1, j = 1; i <= top_L; ++i, ++j) 2012 for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j)
2025 { 2013 {
2026 if (U->verboseErrors) 2014 if (U->verboseErrors)
2027 { 2015 {
@@ -2034,13 +2022,11 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
2034 break; 2022 break;
2035 } 2023 }
2036 } 2024 }
2037 STACK_CHECK(L, 0); 2025 STACK_CHECK(L1, 0);
2038
2039 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
2040 2026
2041 if (copyok) 2027 if (copyok)
2042 { 2028 {
2043 STACK_CHECK(L2, n + 1); 2029 STACK_CHECK(L2, n_ + 1);
2044 // Remove the cache table. Persistent caching would cause i.e. multiple 2030 // Remove the cache table. Persistent caching would cause i.e. multiple
2045 // messages passed in the same table to use the same table also in receiving end. 2031 // messages passed in the same table to use the same table also in receiving end.
2046 lua_remove(L2, top_L2 + 1); 2032 lua_remove(L2, top_L2 + 1);
@@ -2055,74 +2041,108 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
2055 2041
2056// ################################################################################################# 2042// #################################################################################################
2057 2043
2058[[nodiscard]] InterCopyResult luaG_inter_move(Universe* U, Source L, Dest L2, int n_, LookupMode mode_) 2044[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const
2059{ 2045{
2060 InterCopyResult const ret{ luaG_inter_copy(U, L, L2, n_, mode_) }; 2046 InterCopyResult const ret{ inter_copy(n_) };
2061 lua_pop( L, n_); 2047 lua_pop( L1, n_);
2062 return ret; 2048 return ret;
2063} 2049}
2064 2050
2065// ################################################################################################# 2051// #################################################################################################
2066 2052
2067// transfers stuff from L->_G["package"] to L2->_G["package"] 2053// transfers stuff from L1->_G["package"] to L2->_G["package"]
2068// returns InterCopyResult::Success if everything is fine 2054// returns InterCopyResult::Success if everything is fine
2069// returns InterCopyResult::Error if pushed an error message in L 2055// returns InterCopyResult::Error if pushed an error message in L1
2070// else raise an error in L 2056// else raise an error in L1
2071[[nodiscard]] InterCopyResult luaG_inter_copy_package(Universe* U, Source L, Dest L2, int package_idx_, LookupMode mode_) 2057[[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const
2072{ 2058{
2073 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END)); 2059 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END));
2074 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 2060
2075 // package 2061 class OnExit
2076 STACK_CHECK_START_REL(L, 0);
2077 STACK_CHECK_START_REL(L2, 0);
2078 package_idx_ = lua_absindex(L, package_idx_);
2079 if (lua_type(L, package_idx_) != LUA_TTABLE)
2080 { 2062 {
2081 lua_pushfstring(L, "expected package as table, got %s", luaL_typename(L, package_idx_)); 2063 private:
2082 STACK_CHECK(L, 1); 2064
2065 lua_State* const L2;
2066 int const top_L2;
2067 DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
2068
2069 public:
2070
2071 OnExit(Universe* U_, lua_State* L2_)
2072 : L2{ L2_ }
2073 , top_L2{ lua_gettop(L2) }
2074 DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
2075 {
2076 }
2077
2078 ~OnExit()
2079 {
2080 lua_settop(L2, top_L2);
2081 }
2082 } onExit{ U, L2 };
2083
2084 STACK_CHECK_START_REL(L1, 0);
2085 if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE)
2086 {
2087 lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i));
2088 STACK_CHECK(L1, 1);
2083 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later 2089 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
2084 if (mode_ == LookupMode::LaneBody) 2090 if (mode == LookupMode::LaneBody)
2085 { 2091 {
2086 lua_error(L); // doesn't return 2092 lua_error(L1); // doesn't return
2087 } 2093 }
2088 return InterCopyResult::Error; 2094 return InterCopyResult::Error;
2089 } 2095 }
2090 lua_getglobal(L2, "package"); 2096 lua_getglobal(L2, "package"); // TODO: use _R._LOADED.package instead of _G.package
2091 if (!lua_isnil(L2, -1)) // package library not loaded: do nothing 2097 if (lua_isnil(L2, -1)) // package library not loaded: do nothing
2092 { 2098 {
2093 // package.loaders is renamed package.searchers in Lua 5.2 2099 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END));
2094 // but don't copy it anyway, as the function names change depending on the slot index! 2100 STACK_CHECK(L1, 0);
2095 // users should provide an on_state_create function to setup custom loaders instead 2101 return InterCopyResult::Success;
2096 // don't copy package.preload in keeper states (they don't know how to translate functions) 2102 }
2097 char const* entries[] = { "path", "cpath", (mode_ == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; 2103
2098 for (char const* const entry : entries) 2104 InterCopyResult result{ InterCopyResult::Success };
2105 // package.loaders is renamed package.searchers in Lua 5.2
2106 // but don't copy it anyway, as the function names change depending on the slot index!
2107 // users should provide an on_state_create function to setup custom loaders instead
2108 // don't copy package.preload in keeper states (they don't know how to translate functions)
2109 char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
2110 for (char const* const entry : entries)
2111 {
2112 if (!entry)
2113 {
2114 continue;
2115 }
2116 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entry));
2117 lua_getfield(L1, L1_i, entry);
2118 if (lua_isnil(L1, -1))
2119 {
2120 lua_pop(L1, 1);
2121 }
2122 else
2099 { 2123 {
2100 if (!entry)
2101 { 2124 {
2102 continue; 2125 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
2126 result = inter_move(1); // moves the entry to L2
2127 STACK_CHECK(L1, 0);
2103 } 2128 }
2104 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entry)); 2129 if (result == InterCopyResult::Success)
2105 lua_getfield(L, package_idx_, entry);
2106 if (lua_isnil(L, -1))
2107 { 2130 {
2108 lua_pop(L, 1); 2131 lua_setfield(L2, -2, entry); // set package[entry]
2109 } 2132 }
2110 else 2133 else
2111 { 2134 {
2112 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 2135 lua_pushfstring(L1, "failed to copy package entry %s", entry);
2113 std::ignore = luaG_inter_move(U, L, L2, 1, mode_); // moves the entry to L2 2136 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
2114 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); 2137 if (mode == LookupMode::LaneBody)
2115 lua_setfield(L2, -2, entry); // set package[entry] 2138 {
2139 lua_error(L1); // doesn't return
2140 }
2141 lua_pop(L1, 1);
2142 break;
2116 } 2143 }
2117 } 2144 }
2118 } 2145 }
2119 else 2146 STACK_CHECK(L1, 0);
2120 { 2147 return result;
2121 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END));
2122 }
2123 lua_pop(L2, 1);
2124 STACK_CHECK(L2, 0);
2125 STACK_CHECK(L, 0);
2126 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
2127 return InterCopyResult::Success;
2128} 2148}
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_);
19 19
20enum class VT 20enum class VT
21{ 21{
22 NORMAL, 22 NORMAL, // keep this one first so that it's the value we get when we default-construct
23 KEY, 23 KEY,
24 METATABLE 24 METATABLE
25}; 25};
@@ -60,14 +60,16 @@ struct InterCopyContext
60 void copy_func() const; 60 void copy_func() const;
61 void copy_cached_func() const; 61 void copy_cached_func() const;
62 void inter_copy_keyvaluepair() const; 62 void inter_copy_keyvaluepair() const;
63
64 public:
65
66 [[nodiscard]] InterCopyResult inter_copy_package() const;
67 [[nodiscard]] InterCopyResult inter_copy(int n_) const;
68 [[nodiscard]] InterCopyResult inter_move(int n_) const;
63}; 69};
64 70
65// ################################################################################################ 71// ################################################################################################
66 72
67[[nodiscard]] InterCopyResult luaG_inter_copy_package(Universe* U, Source L, Dest L2, int package_idx_, LookupMode mode_);
68[[nodiscard]] InterCopyResult luaG_inter_copy(Universe* U, Source L, Dest L2, int n, LookupMode mode_);
69[[nodiscard]] InterCopyResult luaG_inter_move(Universe* U, Source L, Dest L2, int n, LookupMode mode_);
70
71[[nodiscard]] int luaG_nameof(lua_State* L); 73[[nodiscard]] int luaG_nameof(lua_State* L);
72 74
73void populate_func_lookup_table(lua_State* L, int _i, char const* _name); 75void 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
188[[nodiscard]] Universe* universe_get(lua_State* L); 188[[nodiscard]] Universe* universe_get(lua_State* L);
189[[nodiscard]] Universe* universe_create(lua_State* L); 189[[nodiscard]] Universe* universe_create(lua_State* L);
190void universe_store(lua_State* L, Universe* U); 190void universe_store(lua_State* L, Universe* U);
191
192// ################################################################################################
193
194#if USE_DEBUG_SPEW()
195class DebugSpewIndentScope
196{
197 private:
198
199 Universe* const U;
200
201 public:
202
203 static char const* const debugspew_indent;
204
205 DebugSpewIndentScope(Universe* U_)
206 : U{ U_ }
207 {
208 if (U)
209 U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed);
210 }
211
212 ~DebugSpewIndentScope()
213 {
214 if (U)
215 U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed);
216 }
217};
218#endif // USE_DEBUG_SPEW()