diff options
-rw-r--r-- | deep_test/deeptest.lua | 5 | ||||
-rw-r--r-- | docs/index.html | 21 | ||||
-rw-r--r-- | src/lanes.cpp | 134 | ||||
-rw-r--r-- | src/lanes.lua | 22 | ||||
-rw-r--r-- | src/lanes_private.h | 2 |
5 files changed, 114 insertions, 70 deletions
diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua index 09b638c..33de003 100644 --- a/deep_test/deeptest.lua +++ b/deep_test/deeptest.lua | |||
@@ -7,7 +7,7 @@ local dt = lanes.require "deep_test" | |||
7 | local test_deep = true | 7 | local test_deep = true |
8 | local test_clonable = true | 8 | local test_clonable = true |
9 | local test_uvtype = "string" | 9 | local test_uvtype = "string" |
10 | local nupvals = _VERSION == "Lua 5.4" and 3 or 1 | 10 | local nupvals = _VERSION == "Lua 5.4" and 2 or 1 |
11 | 11 | ||
12 | local makeUserValue = function( obj_) | 12 | local makeUserValue = function( obj_) |
13 | if test_uvtype == "string" then | 13 | if test_uvtype == "string" then |
@@ -43,8 +43,7 @@ local performTest = function( obj_) | |||
43 | obj_:setuv( 1, makeUserValue( obj_)) | 43 | obj_:setuv( 1, makeUserValue( obj_)) |
44 | -- lua 5.4 supports multiple uservalues of arbitrary types | 44 | -- lua 5.4 supports multiple uservalues of arbitrary types |
45 | if nupvals > 1 then | 45 | if nupvals > 1 then |
46 | -- keep uv #2 as nil | 46 | obj_:setuv( 2, "ENDUV") |
47 | obj_:setuv( 3, "ENDUV") | ||
48 | end | 47 | end |
49 | 48 | ||
50 | local t = | 49 | local t = |
diff --git a/docs/index.html b/docs/index.html index ed1c367..f08947e 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -684,7 +684,7 @@ | |||
684 | <td>table</td> | 684 | <td>table</td> |
685 | <td> | 685 | <td> |
686 | Sets the globals table for the launched threads. This can be used for giving them constants. The key/value pairs of <tt>table</tt> are transfered in the lane globals after the libraries have been loaded and the modules required. | 686 | Sets the globals table for the launched threads. This can be used for giving them constants. The key/value pairs of <tt>table</tt> are transfered in the lane globals after the libraries have been loaded and the modules required. |
687 | <br/> | 687 | <br /> |
688 | The global values of different lanes are in no manner connected; modifying one will only affect the particular lane. | 688 | The global values of different lanes are in no manner connected; modifying one will only affect the particular lane. |
689 | </td> | 689 | </td> |
690 | </tr> | 690 | </tr> |
@@ -698,11 +698,20 @@ | |||
698 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. | 698 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. |
699 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. | 699 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. |
700 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). | 700 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). |
701 | <br/> | 701 | <br /> |
702 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. | 702 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. |
703 | </td> | 703 | </td> |
704 | </tr> | 704 | </tr> |
705 | <tr id="Tr1" valign=top> | 705 | <tr id=".name" valign=top> |
706 | <td> | ||
707 | <code>.name</code> | ||
708 | </td> | ||
709 | <td>string</td> | ||
710 | <td> | ||
711 | Name of the lane. If <tt>"auto"</tt>, name is built from <tt>ar.short_src:ar.linedefined</tt>. Can be changed later from the inside of the lane with <tt>set_debug_threadname()</tt> (see below). | ||
712 | </td> | ||
713 | </tr> | ||
714 | <tr id=".gc_cb" valign=top> | ||
706 | <td> | 715 | <td> |
707 | <code>.gc_cb</code> | 716 | <code>.gc_cb</code> |
708 | </td> | 717 | </td> |
@@ -719,9 +728,9 @@ | |||
719 | <td> | 728 | <td> |
720 | The priority of lanes generated in the range -3..+3 (default is 0). | 729 | The priority of lanes generated in the range -3..+3 (default is 0). |
721 | These values are a mapping over the actual priority range of the underlying implementation. | 730 | These values are a mapping over the actual priority range of the underlying implementation. |
722 | <br/> | 731 | <br /> |
723 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. | 732 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. |
724 | <br/> | 733 | <br /> |
725 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. | 734 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. |
726 | </td> | 735 | </td> |
727 | </tr> | 736 | </tr> |
@@ -732,7 +741,7 @@ | |||
732 | <td> table</td> | 741 | <td> table</td> |
733 | <td> | 742 | <td> |
734 | Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. | 743 | Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. |
735 | <br/> | 744 | <br /> |
736 | If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> (Lua 5.1)/<tt>searchers</tt> (Lua 5.2) are transfered. | 745 | If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> (Lua 5.1)/<tt>searchers</tt> (Lua 5.2) are transfered. |
737 | </td> | 746 | </td> |
738 | </tr> | 747 | </tr> |
diff --git a/src/lanes.cpp b/src/lanes.cpp index e09956c..bce75a6 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -201,8 +201,8 @@ void Lane::startThread(int priority_) | |||
201 | */ | 201 | */ |
202 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! | 202 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! |
203 | 203 | ||
204 | // intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed | 204 | // intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed |
205 | static void securize_debug_threadname(lua_State* L_, Lane* lane_) | 205 | void Lane::securizeDebugName(lua_State* L_) |
206 | { | 206 | { |
207 | STACK_CHECK_START_REL(L_, 0); | 207 | STACK_CHECK_START_REL(L_, 0); |
208 | STACK_GROW(L_, 3); | 208 | STACK_GROW(L_, 3); |
@@ -212,8 +212,8 @@ static void securize_debug_threadname(lua_State* L_, Lane* lane_) | |||
212 | // we don't care about the actual key, so long as it's unique and can't collide with anything. | 212 | // we don't care about the actual key, so long as it's unique and can't collide with anything. |
213 | lua_newtable(L_); // L_: lane ... {uv} {} | 213 | lua_newtable(L_); // L_: lane ... {uv} {} |
214 | // Lua 5.1 can't do 'lane_->debugName = lua_pushstring(L_, lane_->debugName);' | 214 | // Lua 5.1 can't do 'lane_->debugName = lua_pushstring(L_, lane_->debugName);' |
215 | lua_pushstring(L_, lane_->debugName); // L_: lane ... {uv} {} name | 215 | lua_pushstring(L_, debugName); // L_: lane ... {uv} {} name |
216 | lane_->debugName = lua_tostring(L_, -1); | 216 | debugName = lua_tostring(L_, -1); |
217 | lua_rawset(L_, -3); // L_: lane ... {uv} | 217 | lua_rawset(L_, -3); // L_: lane ... {uv} |
218 | lua_pop(L_, 1); // L_: lane | 218 | lua_pop(L_, 1); // L_: lane |
219 | STACK_CHECK(L_, 0); | 219 | STACK_CHECK(L_, 0); |
@@ -674,23 +674,36 @@ LUAG_FUNC(set_error_reporting) | |||
674 | 674 | ||
675 | // ################################################################################################# | 675 | // ################################################################################################# |
676 | 676 | ||
677 | void Lane::changeDebugName(int nameIdx_) | ||
678 | { | ||
679 | // xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator | ||
680 | static constexpr RegistryUniqueKey hidden_regkey{ 0xA194E2645C57F6DDull }; | ||
681 | nameIdx_ = lua_absindex(L, nameIdx_); | ||
682 | luaL_checktype(L, nameIdx_, LUA_TSTRING); // L: ... "name" ... | ||
683 | STACK_CHECK_START_REL(L, 0); | ||
684 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... | ||
685 | hidden_regkey.setValue(L, [nameIdx = nameIdx_](lua_State* L_) { lua_pushvalue(L_, nameIdx); });// L: ... "name" ... | ||
686 | // keep a direct pointer on the string | ||
687 | debugName = lua_tostring(L, nameIdx_); | ||
688 | // to see VM name in Decoda debugger Virtual Machine window | ||
689 | lua_pushvalue(L, nameIdx_); // L: ... "name" ... "name" | ||
690 | lua_setglobal(L, "decoda_name"); // L: ... "name" ... | ||
691 | // and finally set the OS thread name | ||
692 | THREAD_SETNAME(debugName); | ||
693 | STACK_CHECK(L, 0); | ||
694 | } | ||
695 | |||
696 | // ################################################################################################# | ||
697 | |||
698 | // upvalue #1 is the lane userdata | ||
677 | LUAG_FUNC(set_debug_threadname) | 699 | LUAG_FUNC(set_debug_threadname) |
678 | { | 700 | { |
679 | // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator | ||
680 | constexpr RegistryUniqueKey hidden_regkey{ 0x79C0669AAAE04440ull }; | ||
681 | // C s_lane structure is a light userdata upvalue | 701 | // C s_lane structure is a light userdata upvalue |
682 | Lane* const lane{ lua_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) }; | 702 | Lane* const lane{ lua_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) }; |
683 | luaL_checktype(L_, -1, LUA_TSTRING); // "name" | 703 | LUA_ASSERT(L_, L_ == lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state |
684 | lua_settop(L_, 1); | 704 | lua_settop(L_, 1); |
685 | STACK_CHECK_START_ABS(L_, 1); | 705 | STACK_CHECK_START_REL(L_, 0); |
686 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... | 706 | lane->changeDebugName(-1); |
687 | hidden_regkey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); | ||
688 | STACK_CHECK(L_, 1); | ||
689 | lane->debugName = lua_tostring(L_, -1); | ||
690 | // keep a direct pointer on the string | ||
691 | THREAD_SETNAME(lane->debugName); | ||
692 | // to see VM name in Decoda debugger Virtual Machine window | ||
693 | lua_setglobal(L_, "decoda_name"); // | ||
694 | STACK_CHECK(L_, 0); | 707 | STACK_CHECK(L_, 0); |
695 | return 0; | 708 | return 0; |
696 | } | 709 | } |
@@ -911,13 +924,14 @@ static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull }; | |||
911 | // , [package_tbl] | 924 | // , [package_tbl] |
912 | // , [required_tbl] | 925 | // , [required_tbl] |
913 | // , [gc_cb_func] | 926 | // , [gc_cb_func] |
927 | // , [name] | ||
914 | // [, ... args ...]) | 928 | // [, ... args ...]) |
915 | // | 929 | // |
916 | // Upvalues: metatable to use for 'lane_ud' | 930 | // Upvalues: metatable to use for 'lane_ud' |
917 | // | 931 | // |
918 | LUAG_FUNC(lane_new) | 932 | LUAG_FUNC(lane_new) |
919 | { | 933 | { |
920 | // first 7 args: func libs priority globals package required gc_cb | 934 | // first 8 args: func libs priority globals package required gc_cb name |
921 | char const* const libs_str{ lua_tostring(L_, 2) }; | 935 | char const* const libs_str{ lua_tostring(L_, 2) }; |
922 | bool const have_priority{ !lua_isnoneornil(L_, 3) }; | 936 | bool const have_priority{ !lua_isnoneornil(L_, 3) }; |
923 | int const priority{ have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault }; | 937 | int const priority{ have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault }; |
@@ -925,8 +939,9 @@ LUAG_FUNC(lane_new) | |||
925 | int const package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 }; | 939 | int const package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 }; |
926 | int const required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 }; | 940 | int const required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 }; |
927 | int const gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 }; | 941 | int const gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 }; |
942 | int const name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 }; | ||
928 | 943 | ||
929 | static constexpr int kFixedArgsIdx{ 7 }; | 944 | static constexpr int kFixedArgsIdx{ 8 }; |
930 | int const nargs{ lua_gettop(L_) - kFixedArgsIdx }; | 945 | int const nargs{ lua_gettop(L_) - kFixedArgsIdx }; |
931 | Universe* const U{ universe_get(L_) }; | 946 | Universe* const U{ universe_get(L_) }; |
932 | LUA_ASSERT(L_, nargs >= 0); | 947 | LUA_ASSERT(L_, nargs >= 0); |
@@ -942,7 +957,7 @@ LUAG_FUNC(lane_new) | |||
942 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(U))); | 957 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(U))); |
943 | 958 | ||
944 | // populate with selected libraries at the same time. | 959 | // populate with selected libraries at the same time. |
945 | lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L_: [7 args] ... L2: | 960 | lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L_: [8 args] ... L2: |
946 | STACK_CHECK_START_REL(L2, 0); | 961 | STACK_CHECK_START_REL(L2, 0); |
947 | 962 | ||
948 | // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) | 963 | // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) |
@@ -957,14 +972,16 @@ LUAG_FUNC(lane_new) | |||
957 | lua_State* const m_L; | 972 | lua_State* const m_L; |
958 | Lane* m_lane{ nullptr }; | 973 | Lane* m_lane{ nullptr }; |
959 | int const m_gc_cb_idx; | 974 | int const m_gc_cb_idx; |
975 | int const m_name_idx; | ||
960 | DEBUGSPEW_CODE(Universe* const U); | 976 | DEBUGSPEW_CODE(Universe* const U); |
961 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); | 977 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); |
962 | 978 | ||
963 | public: | 979 | public: |
964 | OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_)) | 980 | OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_, int name_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_)) |
965 | : m_L{ L_ } | 981 | : m_L{ L_ } |
966 | , m_lane{ lane_ } | 982 | , m_lane{ lane_ } |
967 | , m_gc_cb_idx{ gc_cb_idx_ } | 983 | , m_gc_cb_idx{ gc_cb_idx_ } |
984 | , m_name_idx{ name_idx_ } | ||
968 | DEBUGSPEW_COMMA_PARAM(U{ U_ }) | 985 | DEBUGSPEW_COMMA_PARAM(U{ U_ }) |
969 | DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) | 986 | DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) |
970 | { | 987 | { |
@@ -1019,6 +1036,24 @@ LUAG_FUNC(lane_new) | |||
1019 | } | 1036 | } |
1020 | 1037 | ||
1021 | lua_setiuservalue(m_L, -2, 1); // m_L: ... lane | 1038 | lua_setiuservalue(m_L, -2, 1); // m_L: ... lane |
1039 | |||
1040 | lua_State* L2{ m_lane->L }; | ||
1041 | STACK_CHECK_START_REL(L2, 0); | ||
1042 | char const* const debugName{ (m_name_idx > 0) ? lua_tostring(m_L, m_name_idx) : nullptr }; | ||
1043 | if (debugName) | ||
1044 | { | ||
1045 | if (strcmp(debugName, "auto") != 0) { | ||
1046 | lua_pushstring(L2, debugName); // m_L: ... lane L2: "<name>" | ||
1047 | } else { | ||
1048 | lua_Debug ar; | ||
1049 | lua_pushvalue(m_L, 1); // m_L: ... lane func | ||
1050 | lua_getinfo(m_L, ">S", &ar); // m_L: ... lane | ||
1051 | lua_pushfstring(L2, "%s:%d", ar.short_src, ar.linedefined); // m_L: ... lane L2: "<name>" | ||
1052 | } | ||
1053 | m_lane->changeDebugName(-1); | ||
1054 | lua_pop(L2, 1); // m_L: ... lane L2: | ||
1055 | } | ||
1056 | STACK_CHECK(L2, 0); | ||
1022 | STACK_CHECK(m_L, 1); | 1057 | STACK_CHECK(m_L, 1); |
1023 | } | 1058 | } |
1024 | 1059 | ||
@@ -1029,7 +1064,7 @@ LUAG_FUNC(lane_new) | |||
1029 | m_lane->ready.count_down(); | 1064 | m_lane->ready.count_down(); |
1030 | m_lane = nullptr; | 1065 | m_lane = nullptr; |
1031 | } | 1066 | } |
1032 | } onExit{ L_, lane, gc_cb_idx DEBUGSPEW_COMMA_PARAM(U) }; | 1067 | } onExit{ L_, lane, gc_cb_idx, name_idx DEBUGSPEW_COMMA_PARAM(U) }; |
1033 | // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation | 1068 | // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation |
1034 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(U))); | 1069 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(U))); |
1035 | lane->startThread(priority); | 1070 | lane->startThread(priority); |
@@ -1038,11 +1073,6 @@ LUAG_FUNC(lane_new) | |||
1038 | STACK_GROW(L_, 3); | 1073 | STACK_GROW(L_, 3); |
1039 | STACK_CHECK_START_REL(L_, 0); | 1074 | STACK_CHECK_START_REL(L_, 0); |
1040 | 1075 | ||
1041 | // give a default "Lua" name to the thread to see VM name in Decoda debugger | ||
1042 | lua_pushfstring(L2, "Lane #%p", L2); // L_: [7 args] args... L2: "<name>" | ||
1043 | lua_setglobal(L2, "decoda_name"); // L_: [7 args] args... L2: | ||
1044 | LUA_ASSERT(L_, lua_gettop(L2) == 0); | ||
1045 | |||
1046 | // package | 1076 | // package |
1047 | if (package_idx != 0) { | 1077 | if (package_idx != 0) { |
1048 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(U))); | 1078 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(U))); |
@@ -1062,8 +1092,8 @@ LUAG_FUNC(lane_new) | |||
1062 | raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, required_idx)); | 1092 | raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, required_idx)); |
1063 | } | 1093 | } |
1064 | 1094 | ||
1065 | lua_pushnil(L_); // L_: [7 args] args... nil L2: | 1095 | lua_pushnil(L_); // L_: [8 args] args... nil L2: |
1066 | while (lua_next(L_, required_idx) != 0) { // L_: [7 args] args... n "modname" L2: | 1096 | while (lua_next(L_, required_idx) != 0) { // L_: [8 args] args... n "modname" L2: |
1067 | if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != nbRequired) { | 1097 | if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != nbRequired) { |
1068 | raise_luaL_error(L_, "required module list should be a list of strings"); | 1098 | raise_luaL_error(L_, "required module list should be a list of strings"); |
1069 | } else { | 1099 | } else { |
@@ -1073,30 +1103,30 @@ LUAG_FUNC(lane_new) | |||
1073 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(U), name)); | 1103 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(U), name)); |
1074 | 1104 | ||
1075 | // require the module in the target lane | 1105 | // require the module in the target lane |
1076 | lua_getglobal(L2, "require"); // L_: [7 args] args... n "modname" L2: require()? | 1106 | lua_getglobal(L2, "require"); // L_: [8 args] args... n "modname" L2: require()? |
1077 | if (lua_isnil(L2, -1)) { | 1107 | if (lua_isnil(L2, -1)) { |
1078 | lua_pop(L2, 1); // L_: [7 args] args... n "modname" L2: | 1108 | lua_pop(L2, 1); // L_: [8 args] args... n "modname" L2: |
1079 | raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first"); | 1109 | raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first"); |
1080 | } else { | 1110 | } else { |
1081 | lua_pushlstring(L2, name, len); // L_: [7 args] args... n "modname" L2: require() name | 1111 | lua_pushlstring(L2, name, len); // L_: [8 args] args... n "modname" L2: require() name |
1082 | if (lua_pcall(L2, 1, 1, 0) != LUA_OK) { // L_: [7 args] args... n "modname" L2: ret/errcode | 1112 | if (lua_pcall(L2, 1, 1, 0) != LUA_OK) { // L_: [8 args] args... n "modname" L2: ret/errcode |
1083 | // propagate error to main state if any | 1113 | // propagate error to main state if any |
1084 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; | 1114 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; |
1085 | std::ignore = c.inter_move(1); // L_: [7 args] args... n "modname" error L2: | 1115 | std::ignore = c.inter_move(1); // L_: [8 args] args... n "modname" error L2: |
1086 | raise_lua_error(L_); | 1116 | raise_lua_error(L_); |
1087 | } | 1117 | } |
1088 | // here the module was successfully required // L_: [7 args] args... n "modname" L2: ret | 1118 | // here the module was successfully required // L_: [8 args] args... n "modname" L2: ret |
1089 | // after requiring the module, register the functions it exported in our name<->function database | 1119 | // after requiring the module, register the functions it exported in our name<->function database |
1090 | populate_func_lookup_table(L2, -1, name); | 1120 | populate_func_lookup_table(L2, -1, name); |
1091 | lua_pop(L2, 1); // L_: [7 args] args... n "modname" L2: | 1121 | lua_pop(L2, 1); // L_: [8 args] args... n "modname" L2: |
1092 | } | 1122 | } |
1093 | } | 1123 | } |
1094 | lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n | 1124 | lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n |
1095 | ++nbRequired; | 1125 | ++nbRequired; |
1096 | } // L_: [7 args] args... | 1126 | } // L_: [8 args] args... |
1097 | } | 1127 | } |
1098 | STACK_CHECK(L_, 0); | 1128 | STACK_CHECK(L_, 0); |
1099 | STACK_CHECK(L2, 0); // L_: [7 args] args... L2: | 1129 | STACK_CHECK(L2, 0); // L_: [8 args] args... L2: |
1100 | 1130 | ||
1101 | // Appending the specified globals to the global environment | 1131 | // Appending the specified globals to the global environment |
1102 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... | 1132 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... |
@@ -1108,17 +1138,17 @@ LUAG_FUNC(lane_new) | |||
1108 | } | 1138 | } |
1109 | 1139 | ||
1110 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1140 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1111 | lua_pushnil(L_); // L_: [7 args] args... nil L2: | 1141 | lua_pushnil(L_); // L_: [8 args] args... nil L2: |
1112 | // Lua 5.2 wants us to push the globals table on the stack | 1142 | // Lua 5.2 wants us to push the globals table on the stack |
1113 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; | 1143 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; |
1114 | lua_pushglobaltable(L2); // L_: [7 args] args... nil L2: _G | 1144 | lua_pushglobaltable(L2); // L_: [8 args] args... nil L2: _G |
1115 | while (lua_next(L_, globals_idx)) { // L_: [7 args] args... k v L2: _G | 1145 | while (lua_next(L_, globals_idx)) { // L_: [8 args] args... k v L2: _G |
1116 | std::ignore = c.inter_copy(2); // L_: [7 args] args... k v L2: _G k v | 1146 | std::ignore = c.inter_copy(2); // L_: [8 args] args... k v L2: _G k v |
1117 | // assign it in L2's globals table | 1147 | // assign it in L2's globals table |
1118 | lua_rawset(L2, -3); // L_: [7 args] args... k v L2: _G | 1148 | lua_rawset(L2, -3); // L_: [8 args] args... k v L2: _G |
1119 | lua_pop(L_, 1); // L_: [7 args] args... k | 1149 | lua_pop(L_, 1); // L_: [8 args] args... k |
1120 | } // L_: [7 args] args... | 1150 | } // L_: [8 args] args... |
1121 | lua_pop(L2, 1); // L_: [7 args] args... L2: | 1151 | lua_pop(L2, 1); // L_: [8 args] args... L2: |
1122 | } | 1152 | } |
1123 | STACK_CHECK(L_, 0); | 1153 | STACK_CHECK(L_, 0); |
1124 | STACK_CHECK(L2, 0); | 1154 | STACK_CHECK(L2, 0); |
@@ -1128,16 +1158,16 @@ LUAG_FUNC(lane_new) | |||
1128 | if (func_type == LuaType::FUNCTION) { | 1158 | if (func_type == LuaType::FUNCTION) { |
1129 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(U))); | 1159 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(U))); |
1130 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1160 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1131 | lua_pushvalue(L_, 1); // L_: [7 args] args... func L2: | 1161 | lua_pushvalue(L_, 1); // L_: [8 args] args... func L2: |
1132 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; | 1162 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; |
1133 | InterCopyResult const res{ c.inter_move(1) }; // L_: [7 args] args... L2: func | 1163 | InterCopyResult const res{ c.inter_move(1) }; // L_: [8 args] args... L2: func |
1134 | if (res != InterCopyResult::Success) { | 1164 | if (res != InterCopyResult::Success) { |
1135 | raise_luaL_error(L_, "tried to copy unsupported types"); | 1165 | raise_luaL_error(L_, "tried to copy unsupported types"); |
1136 | } | 1166 | } |
1137 | } else if (func_type == LuaType::STRING) { | 1167 | } else if (func_type == LuaType::STRING) { |
1138 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(U))); | 1168 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(U))); |
1139 | // compile the string | 1169 | // compile the string |
1140 | if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) { // L_: [7 args] args... L2: func | 1170 | if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: func |
1141 | raise_luaL_error(L_, "error when parsing lane function code"); | 1171 | raise_luaL_error(L_, "error when parsing lane function code"); |
1142 | } | 1172 | } |
1143 | } else { | 1173 | } else { |
@@ -1152,7 +1182,7 @@ LUAG_FUNC(lane_new) | |||
1152 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(U))); | 1182 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(U))); |
1153 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1183 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1154 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; | 1184 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; |
1155 | InterCopyResult const res{ c.inter_move(nargs) }; // L_: [7 args] L2: func args... | 1185 | InterCopyResult const res{ c.inter_move(nargs) }; // L_: [8 args] L2: func args... |
1156 | if (res != InterCopyResult::Success) { | 1186 | if (res != InterCopyResult::Success) { |
1157 | raise_luaL_error(L_, "tried to copy unsupported types"); | 1187 | raise_luaL_error(L_, "tried to copy unsupported types"); |
1158 | } | 1188 | } |
@@ -1161,12 +1191,12 @@ LUAG_FUNC(lane_new) | |||
1161 | LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); | 1191 | LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); |
1162 | 1192 | ||
1163 | // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). | 1193 | // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). |
1164 | kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // L_: [7 args] L2: func args... | 1194 | kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // L_: [8 args] L2: func args... |
1165 | STACK_CHECK(L2, 1 + nargs); | 1195 | STACK_CHECK(L2, 1 + nargs); |
1166 | 1196 | ||
1167 | STACK_CHECK_RESET_REL(L_, 0); | 1197 | STACK_CHECK_RESET_REL(L_, 0); |
1168 | // all went well, the lane's thread can start working | 1198 | // all went well, the lane's thread can start working |
1169 | onExit.success(); // L_: [7 args] lane L2: <living its own life> | 1199 | onExit.success(); // L_: [8 args] lane L2: <living its own life> |
1170 | // we should have the lane userdata on top of the stack | 1200 | // we should have the lane userdata on top of the stack |
1171 | STACK_CHECK(L_, 1); | 1201 | STACK_CHECK(L_, 1); |
1172 | return 1; | 1202 | return 1; |
@@ -1298,7 +1328,7 @@ LUAG_FUNC(thread_join) | |||
1298 | Universe* const U{ lane->U }; | 1328 | Universe* const U{ lane->U }; |
1299 | // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed | 1329 | // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed |
1300 | // so store it in the userdata uservalue at a key that can't possibly collide | 1330 | // so store it in the userdata uservalue at a key that can't possibly collide |
1301 | securize_debug_threadname(L_, lane); | 1331 | lane->securizeDebugName(L_); |
1302 | switch (lane->status) { | 1332 | switch (lane->status) { |
1303 | case Lane::Done: | 1333 | case Lane::Done: |
1304 | { | 1334 | { |
diff --git a/src/lanes.lua b/src/lanes.lua index 8a2592b..f95dddf 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -208,25 +208,29 @@ end | |||
208 | 208 | ||
209 | local opt_validators = | 209 | local opt_validators = |
210 | { | 210 | { |
211 | priority = function(v_) | 211 | gc_cb = function(v_) |
212 | local tv = type(v_) | 212 | local tv = type(v_) |
213 | return (tv == "number") and v_ or raise_option_error("priority", tv, v_) | 213 | return (tv == "function") and v_ or raise_option_error("gc_cb", tv, v_) |
214 | end, | 214 | end, |
215 | globals = function(v_) | 215 | globals = function(v_) |
216 | local tv = type(v_) | 216 | local tv = type(v_) |
217 | return (tv == "table") and v_ or raise_option_error("globals", tv, v_) | 217 | return (tv == "table") and v_ or raise_option_error("globals", tv, v_) |
218 | end, | 218 | end, |
219 | name = function(v_) | ||
220 | local tv = type(v_) | ||
221 | return (tv == "string") and v_ or raise_option_error("name", tv, v_) | ||
222 | end, | ||
219 | package = function(v_) | 223 | package = function(v_) |
220 | local tv = type(v_) | 224 | local tv = type(v_) |
221 | return (tv == "table") and v_ or raise_option_error("package", tv, v_) | 225 | return (tv == "table") and v_ or raise_option_error("package", tv, v_) |
222 | end, | 226 | end, |
223 | required = function(v_) | 227 | priority = function(v_) |
224 | local tv = type(v_) | 228 | local tv = type(v_) |
225 | return (tv == "table") and v_ or raise_option_error("required", tv, v_) | 229 | return (tv == "number") and v_ or raise_option_error("priority", tv, v_) |
226 | end, | 230 | end, |
227 | gc_cb = function(v_) | 231 | required = function(v_) |
228 | local tv = type(v_) | 232 | local tv = type(v_) |
229 | return (tv == "function") and v_ or raise_option_error("gc_cb", tv, v_) | 233 | return (tv == "table") and v_ or raise_option_error("required", tv, v_) |
230 | end | 234 | end |
231 | } | 235 | } |
232 | 236 | ||
@@ -338,10 +342,10 @@ local gen = function(...) | |||
338 | end | 342 | end |
339 | 343 | ||
340 | local core_lane_new = assert(core.lane_new) | 344 | local core_lane_new = assert(core.lane_new) |
341 | local priority, globals, package, required, gc_cb = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb | 345 | local priority, globals, package, required, gc_cb, name = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name |
342 | return function(...) | 346 | return function(...) |
343 | -- must pass functions args last else they will be truncated to the first one | 347 | -- must pass functions args last else they will be truncated to the first one |
344 | return core_lane_new(func, libs, priority, globals, package, required, gc_cb, ...) | 348 | return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, ...) |
345 | end | 349 | end |
346 | end -- gen() | 350 | end -- gen() |
347 | 351 | ||
@@ -569,7 +573,7 @@ local configure_timers = function() | |||
569 | end | 573 | end |
570 | end | 574 | end |
571 | end -- timer_body() | 575 | end -- timer_body() |
572 | timer_lane = gen("*", { package= {}, priority = core.max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... | 576 | timer_lane = gen("*", { package= {}, priority = core.max_prio, name = "LanesTimer"}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... |
573 | end -- first_time | 577 | end -- first_time |
574 | 578 | ||
575 | ----- | 579 | ----- |
diff --git a/src/lanes_private.h b/src/lanes_private.h index 01630ba..309b632 100644 --- a/src/lanes_private.h +++ b/src/lanes_private.h | |||
@@ -89,6 +89,8 @@ class Lane | |||
89 | [[nodiscard]] bool waitForCompletion(lua_Duration duration_); | 89 | [[nodiscard]] bool waitForCompletion(lua_Duration duration_); |
90 | void startThread(int priority_); | 90 | void startThread(int priority_); |
91 | void pushThreadStatus(lua_State* L_); | 91 | void pushThreadStatus(lua_State* L_); |
92 | void changeDebugName(int nameIdx_); | ||
93 | void securizeDebugName(lua_State* L_); | ||
92 | }; | 94 | }; |
93 | 95 | ||
94 | // xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator | 96 | // xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator |