aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-03-29 16:21:56 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-03-29 16:21:56 +0100
commit336935ed4920e1359ab30261a5a028a70d379241 (patch)
tree147c901be2c28cda3422953d4da6939240696801 /src
parentacb203829bfd12b4cc0c90f82cb70a4d111bce46 (diff)
downloadlanes-336935ed4920e1359ab30261a5a028a70d379241.tar.gz
lanes-336935ed4920e1359ab30261a5a028a70d379241.tar.bz2
lanes-336935ed4920e1359ab30261a5a028a70d379241.zip
C++ migration: parallelize lane setup and OS thread warmup
Diffstat (limited to 'src')
-rw-r--r--src/lanes.cpp241
-rw-r--r--src/lanes_private.h5
-rw-r--r--src/universe.cpp2
3 files changed, 146 insertions, 102 deletions
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 079b880..8a76217 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -410,6 +410,8 @@ static void selfdestruct_add(Lane* lane_)
410 lane_->U->selfdestruct_first = lane_; 410 lane_->U->selfdestruct_first = lane_;
411} 411}
412 412
413// ###############################################################################################
414
413/* 415/*
414 * A free-running lane has ended; remove it from selfdestruct chain 416 * A free-running lane has ended; remove it from selfdestruct chain
415 */ 417 */
@@ -443,10 +445,12 @@ static bool selfdestruct_remove(Lane* lane_)
443 return found; 445 return found;
444} 446}
445 447
448// ###############################################################################################
449
446/* 450/*
447* Process end; cancel any still free-running threads 451* Process end; cancel any still free-running threads
448*/ 452*/
449static int selfdestruct_gc( lua_State* L) 453static int universe_gc( lua_State* L)
450{ 454{
451 Universe* const U{ lua_tofulluserdata<Universe>(L, 1) }; 455 Universe* const U{ lua_tofulluserdata<Universe>(L, 1) };
452 456
@@ -595,6 +599,7 @@ static int selfdestruct_gc( lua_State* L)
595 return 0; 599 return 0;
596} 600}
597 601
602// ###############################################################################################
598 603
599//--- 604//---
600// = _single( [cores_uint=1] ) 605// = _single( [cores_uint=1] )
@@ -624,6 +629,7 @@ LUAG_FUNC( set_singlethreaded)
624#endif 629#endif
625} 630}
626 631
632// ###############################################################################################
627 633
628/* 634/*
629* str= lane_error( error_val|str ) 635* str= lane_error( error_val|str )
@@ -885,78 +891,85 @@ static void thread_cleanup_handler(void* opaque)
885 891
886static THREAD_RETURN_T THREAD_CALLCONV lane_main(void* vs) 892static THREAD_RETURN_T THREAD_CALLCONV lane_main(void* vs)
887{ 893{
888 Lane* const lane{ (Lane*) vs }; 894 Lane* lane{ (Lane*) vs };
889 lua_State* const L{ lane->L }; 895 lua_State* const L{ lane->L };
890 // Called with the lane function and arguments on the stack 896 // wait until the launching thread has finished preparing L
891 int const nargs{ lua_gettop(L) - 1 }; 897 lane->m_ready.wait();
892 DEBUGSPEW_CODE(Universe* U = universe_get(L)); 898 int rc{ LUA_ERRRUN };
893 THREAD_MAKE_ASYNCH_CANCELLABLE(); 899 if (lane->status == PENDING) // nothing wrong happened during preparation, we can work
894 THREAD_CLEANUP_PUSH(thread_cleanup_handler, lane); 900 {
895 lane->status = RUNNING; // PENDING -> RUNNING 901 // At this point, the lane function and arguments are on the stack
896 902 int const nargs{ lua_gettop(L) - 1 };
897 // Tie "set_finalizer()" to the state 903 DEBUGSPEW_CODE(Universe* U = universe_get(L));
898 lua_pushcfunction(L, LG_set_finalizer); 904 THREAD_MAKE_ASYNCH_CANCELLABLE();
899 populate_func_lookup_table(L, -1, "set_finalizer"); 905 THREAD_CLEANUP_PUSH(thread_cleanup_handler, lane);
900 lua_setglobal(L, "set_finalizer"); 906 lane->status = RUNNING; // PENDING -> RUNNING
901 907
902 // Tie "set_debug_threadname()" to the state 908 // Tie "set_finalizer()" to the state
903 // But don't register it in the lookup database because of the Lane pointer upvalue 909 lua_pushcfunction(L, LG_set_finalizer);
904 lua_pushlightuserdata(L, lane); 910 populate_func_lookup_table(L, -1, "set_finalizer");
905 lua_pushcclosure(L, LG_set_debug_threadname, 1); 911 lua_setglobal(L, "set_finalizer");
906 lua_setglobal(L, "set_debug_threadname"); 912
907 913 // Tie "set_debug_threadname()" to the state
908 // Tie "cancel_test()" to the state 914 // But don't register it in the lookup database because of the Lane pointer upvalue
909 lua_pushcfunction(L, LG_cancel_test); 915 lua_pushlightuserdata(L, lane);
910 populate_func_lookup_table(L, -1, "cancel_test"); 916 lua_pushcclosure(L, LG_set_debug_threadname, 1);
911 lua_setglobal(L, "cancel_test"); 917 lua_setglobal(L, "set_debug_threadname");
912 918
913 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around 919 // Tie "cancel_test()" to the state
920 lua_pushcfunction(L, LG_cancel_test);
921 populate_func_lookup_table(L, -1, "cancel_test");
922 lua_setglobal(L, "cancel_test");
923
924 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around
914#if ERROR_FULL_STACK 925#if ERROR_FULL_STACK
915 // Tie "set_error_reporting()" to the state 926 // Tie "set_error_reporting()" to the state
916 lua_pushcfunction(L, LG_set_error_reporting); 927 lua_pushcfunction(L, LG_set_error_reporting);
917 populate_func_lookup_table(L, -1, "set_error_reporting"); 928 populate_func_lookup_table(L, -1, "set_error_reporting");
918 lua_setglobal(L, "set_error_reporting"); 929 lua_setglobal(L, "set_error_reporting");
919 930
920 STACK_GROW(L, 1); 931 STACK_GROW(L, 1);
921 lua_pushcfunction(L, lane_error); // func args handler 932 lua_pushcfunction(L, lane_error); // func args handler
922 lua_insert(L, 1); // handler func args 933 lua_insert(L, 1); // handler func args
923#endif // ERROR_FULL_STACK 934#endif // ERROR_FULL_STACK
924 935
925 int rc{ lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK) }; // retvals|err 936 rc = lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err
926 937
927#if ERROR_FULL_STACK 938#if ERROR_FULL_STACK
928 lua_remove(L, 1); // retvals|error 939 lua_remove(L, 1); // retvals|error
929# endif // ERROR_FULL_STACK 940#endif // ERROR_FULL_STACK
930 941
931 // in case of error and if it exists, fetch stack trace from registry and push it 942 // in case of error and if it exists, fetch stack trace from registry and push it
932 push_stack_trace(L, rc, 1); // retvals|error [trace] 943 push_stack_trace(L, rc, 1); // retvals|error [trace]
933 944
934 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), CANCEL_ERROR.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1)))); 945 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), CANCEL_ERROR.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1))));
935 // STACK_DUMP(L); 946 // STACK_DUMP(L);
936 // Call finalizers, if the script has set them up. 947 // Call finalizers, if the script has set them up.
937 //
938 int rc2{ run_finalizers(L, rc) };
939 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name(rc2)));
940 if (rc2 != LUA_OK) // Error within a finalizer!
941 {
942 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
943 rc = rc2; // we're overruling the earlier script error or normal return
944 }
945 lane->waiting_on = nullptr; // just in case
946 if (selfdestruct_remove(lane)) // check and remove (under lock!)
947 {
948 // We're a free-running thread and no-one's there to clean us up.
949 // 948 //
950 lua_close(lane->L); 949 int rc2{ run_finalizers(L, rc) };
950 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name(rc2)));
951 if (rc2 != LUA_OK) // Error within a finalizer!
952 {
953 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
954 rc = rc2; // we're overruling the earlier script error or normal return
955 }
956 lane->waiting_on = nullptr; // just in case
957 if (selfdestruct_remove(lane)) // check and remove (under lock!)
958 {
959 // We're a free-running thread and no-one's there to clean us up.
960 //
961 lua_close(lane->L);
951 962
952 lane->U->selfdestruct_cs.lock(); 963 lane->U->selfdestruct_cs.lock();
953 // done with lua_close(), terminal shutdown sequence may proceed 964 // done with lua_close(), terminal shutdown sequence may proceed
954 --lane->U->selfdestructing_count; 965 --lane->U->selfdestructing_count;
955 lane->U->selfdestruct_cs.unlock(); 966 lane->U->selfdestruct_cs.unlock();
956 967
957 delete lane; 968 delete lane;
969 lane = nullptr;
970 }
958 } 971 }
959 else 972 if (lane)
960 { 973 {
961 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them 974 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
962 975
@@ -1044,19 +1057,19 @@ static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull };
1044// 1057//
1045// Upvalues: metatable to use for 'lane_ud' 1058// Upvalues: metatable to use for 'lane_ud'
1046// 1059//
1047LUAG_FUNC( lane_new) 1060LUAG_FUNC(lane_new)
1048{ 1061{
1049 char const* libs_str = lua_tostring(L, 2); 1062 char const* const libs_str{ lua_tostring(L, 2) };
1050 bool const have_priority{ !lua_isnoneornil(L, 3) }; 1063 bool const have_priority{ !lua_isnoneornil(L, 3) };
1051 int const priority = have_priority ? (int) lua_tointeger(L, 3) : THREAD_PRIO_DEFAULT; 1064 int const priority{ have_priority ? (int) lua_tointeger(L, 3) : THREAD_PRIO_DEFAULT };
1052 int const globals_idx = lua_isnoneornil(L, 4) ? 0 : 4; 1065 int const globals_idx{ lua_isnoneornil(L, 4) ? 0 : 4 };
1053 int const package_idx = lua_isnoneornil(L, 5) ? 0 : 5; 1066 int const package_idx{ lua_isnoneornil(L, 5) ? 0 : 5 };
1054 int const required_idx = lua_isnoneornil(L, 6) ? 0 : 6; 1067 int const required_idx{ lua_isnoneornil(L, 6) ? 0 : 6 };
1055 int const gc_cb_idx = lua_isnoneornil(L, 7) ? 0 : 7; 1068 int const gc_cb_idx{ lua_isnoneornil(L, 7) ? 0 : 7 };
1056 1069
1057#define FIXED_ARGS 7 1070 static constexpr int FIXED_ARGS{ 7 };
1058 int const nargs = lua_gettop(L) - FIXED_ARGS; 1071 int const nargs{ lua_gettop(L) - FIXED_ARGS };
1059 Universe* const U = universe_get( L); 1072 Universe* const U{ universe_get(L) };
1060 ASSERT_L( nargs >= 0); 1073 ASSERT_L( nargs >= 0);
1061 1074
1062 // public Lanes API accepts a generic range -3/+3 1075 // public Lanes API accepts a generic range -3/+3
@@ -1072,7 +1085,42 @@ LUAG_FUNC( lane_new)
1072 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1085 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1073 1086
1074 // populate with selected libraries at the same time 1087 // populate with selected libraries at the same time
1075 lua_State* const L2{ luaG_newstate(U, L, libs_str) }; // L // L2 1088 lua_State* const L2{ luaG_newstate(U, L, libs_str) }; // L // L2
1089
1090 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
1091 Lane* const lane{ new (U) Lane{ U, L2 } };
1092 if (lane == nullptr)
1093 {
1094 return luaL_error(L, "could not create lane: out of memory");
1095 }
1096
1097 // would prefer std::experimental::scope, but it's not universally available
1098 struct OnExit
1099 {
1100 Lane* m_lane{ nullptr };
1101 OnExit(Lane* lane_) : m_lane{ lane_ } {}
1102 ~OnExit()
1103 {
1104 if (m_lane)
1105 {
1106 // leave a single cancel_error on the stack for the caller
1107 lua_settop(m_lane->L, 0);
1108 CANCEL_ERROR.pushKey(m_lane->L);
1109#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1110 MUTEX_LOCK(&m_lane->done_lock);
1111#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1112 m_lane->status = CANCELLED;
1113#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1114 MUTEX_UNLOCK(&m_lane->done_lock);
1115#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1116 // unblock the thread so that it can terminate gracefully
1117 m_lane->m_ready.count_down();
1118 }
1119 }
1120 } onExit{ lane };
1121 // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation
1122 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END));
1123 THREAD_CREATE(&lane->thread, lane_main, lane, priority);
1076 1124
1077 STACK_GROW( L2, nargs + 3); // 1125 STACK_GROW( L2, nargs + 3); //
1078 STACK_CHECK_START_REL(L2, 0); 1126 STACK_CHECK_START_REL(L2, 0);
@@ -1106,7 +1154,7 @@ LUAG_FUNC( lane_new)
1106 return luaL_error(L, "expected required module list as a table, got %s", luaL_typename(L, required_idx)); 1154 return luaL_error(L, "expected required module list as a table, got %s", luaL_typename(L, required_idx));
1107 } 1155 }
1108 1156
1109 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil 1157 lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil
1110 while( lua_next(L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname" 1158 while( lua_next(L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname"
1111 { 1159 {
1112 if (lua_type(L, -1) != LUA_TSTRING || lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != nbRequired) 1160 if (lua_type(L, -1) != LUA_TSTRING || lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != nbRequired)
@@ -1133,7 +1181,7 @@ LUAG_FUNC( lane_new)
1133 if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode 1181 if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode
1134 { 1182 {
1135 // propagate error to main state if any 1183 // propagate error to main state if any
1136 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error 1184 luaG_inter_move(U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error
1137 raise_lua_error(L); 1185 raise_lua_error(L);
1138 } 1186 }
1139 // after requiring the module, register the functions it exported in our name<->function database 1187 // after requiring the module, register the functions it exported in our name<->function database
@@ -1143,11 +1191,11 @@ LUAG_FUNC( lane_new)
1143 } 1191 }
1144 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n 1192 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n
1145 ++ nbRequired; 1193 ++ nbRequired;
1146 } // func libs priority globals package required gc_cb [... args ...] 1194 } // func libs priority globals package required gc_cb [... args ...]
1147 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1195 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1148 } 1196 }
1149 STACK_CHECK(L, 0); 1197 STACK_CHECK(L, 0);
1150 STACK_CHECK( L2, 0); // 1198 STACK_CHECK(L2, 0); //
1151 1199
1152 // Appending the specified globals to the global environment 1200 // Appending the specified globals to the global environment
1153 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... 1201 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
@@ -1161,16 +1209,16 @@ LUAG_FUNC( lane_new)
1161 } 1209 }
1162 1210
1163 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1211 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1164 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil 1212 lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil
1165 // Lua 5.2 wants us to push the globals table on the stack 1213 // Lua 5.2 wants us to push the globals table on the stack
1166 lua_pushglobaltable( L2); // _G 1214 lua_pushglobaltable(L2); // _G
1167 while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v 1215 while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v
1168 { 1216 {
1169 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v 1217 luaG_inter_copy(U, L, L2, 2, eLM_LaneBody); // _G k v
1170 // assign it in L2's globals table 1218 // assign it in L2's globals table
1171 lua_rawset( L2, -3); // _G 1219 lua_rawset(L2, -3); // _G
1172 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k 1220 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k
1173 } // func libs priority globals package required gc_cb [... args ...] 1221 } // func libs priority globals package required gc_cb [... args ...]
1174 lua_pop( L2, 1); // 1222 lua_pop( L2, 1); //
1175 1223
1176 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1224 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
@@ -1185,7 +1233,7 @@ LUAG_FUNC( lane_new)
1185 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1233 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1186 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1234 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1187 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func 1235 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func
1188 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func 1236 res = luaG_inter_move(U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1189 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1237 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1190 if (res != 0) 1238 if (res != 0)
1191 { 1239 {
@@ -1195,7 +1243,7 @@ LUAG_FUNC( lane_new)
1195 else if (lua_type(L, 1) == LUA_TSTRING) 1243 else if (lua_type(L, 1) == LUA_TSTRING)
1196 { 1244 {
1197 // compile the string 1245 // compile the string
1198 if (luaL_loadstring( L2, lua_tostring(L, 1)) != 0) // func 1246 if (luaL_loadstring(L2, lua_tostring(L, 1)) != 0) // func
1199 { 1247 {
1200 return luaL_error(L, "error when parsing lane function code"); 1248 return luaL_error(L, "error when parsing lane function code");
1201 } 1249 }
@@ -1210,7 +1258,7 @@ LUAG_FUNC( lane_new)
1210 int res; 1258 int res;
1211 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1259 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1212 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1260 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1213 res = luaG_inter_move(U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] 1261 res = luaG_inter_move(U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...]
1214 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1262 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1215 if (res != 0) 1263 if (res != 0)
1216 { 1264 {
@@ -1222,12 +1270,6 @@ LUAG_FUNC( lane_new)
1222 STACK_CHECK_RESET_REL(L, 0); 1270 STACK_CHECK_RESET_REL(L, 0);
1223 STACK_CHECK( L2, 1 + nargs); 1271 STACK_CHECK( L2, 1 + nargs);
1224 1272
1225 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
1226 Lane* const lane{ new (U) Lane{ U, L2 } };
1227 if (lane == nullptr)
1228 {
1229 return luaL_error(L, "could not create lane: out of memory");
1230 }
1231 // a Lane full userdata needs a single uservalue 1273 // a Lane full userdata needs a single uservalue
1232 Lane** const ud{ lua_newuserdatauv<Lane*>(L, 1) }; // func libs priority globals package required gc_cb lane 1274 Lane** const ud{ lua_newuserdatauv<Lane*>(L, 1) }; // func libs priority globals package required gc_cb lane
1233 *ud = lane; // don't forget to store the pointer in the userdata! 1275 *ud = lane; // don't forget to store the pointer in the userdata!
@@ -1256,13 +1298,12 @@ LUAG_FUNC( lane_new)
1256 LANE_POINTER_REGKEY.setValue(L2, [lane](lua_State* L) { lua_pushlightuserdata(L, lane); }); // func [... args ...] 1298 LANE_POINTER_REGKEY.setValue(L2, [lane](lua_State* L) { lua_pushlightuserdata(L, lane); }); // func [... args ...]
1257 1299
1258 STACK_CHECK(L, 1); 1300 STACK_CHECK(L, 1);
1259 STACK_CHECK( L2, 1 + nargs); 1301 STACK_CHECK(L2, 1 + nargs);
1260
1261 // TODO: launch thread earlier, sync with a std::latch to parallelize OS thread warmup and L2 preparation
1262 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END));
1263 THREAD_CREATE(&lane->thread, lane_main, lane, priority);
1264
1265 DEBUGSPEW_CODE(--U->debugspew_indent_depth); 1302 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
1303
1304 // all went well, the lane's thread can start working
1305 onExit.m_lane = nullptr;
1306 lane->m_ready.count_down();
1266 return 1; 1307 return 1;
1267} 1308}
1268 1309
@@ -1280,7 +1321,7 @@ LUAG_FUNC( lane_new)
1280// and the issue of canceling/killing threads at gc is not very nice, either 1321// and the issue of canceling/killing threads at gc is not very nice, either
1281// (would easily cause waits at gc cycle, which we don't want). 1322// (would easily cause waits at gc cycle, which we don't want).
1282// 1323//
1283LUAG_FUNC(thread_gc) 1324static int lane_gc(lua_State* L)
1284{ 1325{
1285 bool have_gc_cb{ false }; 1326 bool have_gc_cb{ false };
1286 Lane* lane{ lua_toLane(L, 1) }; // ud 1327 Lane* lane{ lua_toLane(L, 1) }; // ud
@@ -1869,7 +1910,7 @@ LUAG_FUNC(configure)
1869 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1910 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1870 lua_newtable( L); // settings universe mt 1911 lua_newtable( L); // settings universe mt
1871 lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout 1912 lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1872 lua_pushcclosure(L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc 1913 lua_pushcclosure(L, universe_gc, 1); // settings universe mt universe_gc
1873 lua_setfield(L, -2, "__gc"); // settings universe mt 1914 lua_setfield(L, -2, "__gc"); // settings universe mt
1874 lua_setmetatable(L, -2); // settings universe 1915 lua_setmetatable(L, -2); // settings universe
1875 lua_pop(L, 1); // settings 1916 lua_pop(L, 1); // settings
@@ -1940,7 +1981,7 @@ LUAG_FUNC(configure)
1940 // 1981 //
1941 if (luaL_newmetatable(L, "Lane")) // settings M mt 1982 if (luaL_newmetatable(L, "Lane")) // settings M mt
1942 { 1983 {
1943 lua_pushcfunction(L, LG_thread_gc); // settings M mt LG_thread_gc 1984 lua_pushcfunction(L, lane_gc); // settings M mt lane_gc
1944 lua_setfield(L, -2, "__gc"); // settings M mt 1985 lua_setfield(L, -2, "__gc"); // settings M mt
1945 lua_pushcfunction(L, LG_thread_index); // settings M mt LG_thread_index 1986 lua_pushcfunction(L, LG_thread_index); // settings M mt LG_thread_index
1946 lua_setfield(L, -2, "__index"); // settings M mt 1987 lua_setfield(L, -2, "__index"); // settings M mt
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 7c52876..bcc3014 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -1,9 +1,11 @@
1#pragma once 1#pragma once
2 2
3#include "uniquekey.h"
4#include "cancel.h" 3#include "cancel.h"
4#include "uniquekey.h"
5#include "universe.h" 5#include "universe.h"
6 6
7#include <latch>
8
7// NOTE: values to be changed by either thread, during execution, without 9// NOTE: values to be changed by either thread, during execution, without
8// locking, are marked "volatile" 10// locking, are marked "volatile"
9// 11//
@@ -22,6 +24,7 @@ class Lane
22 using enum ThreadStatus; 24 using enum ThreadStatus;
23 25
24 THREAD_T thread; 26 THREAD_T thread;
27 std::latch m_ready{ 1 };
25 // 28 //
26 // M: sub-thread OS thread 29 // M: sub-thread OS thread
27 // S: not used 30 // S: not used
diff --git a/src/universe.cpp b/src/universe.cpp
index c487ac0..66da147 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -60,7 +60,7 @@ Universe* universe_create(lua_State* L)
60 60
61void universe_store(lua_State* L, Universe* U) 61void universe_store(lua_State* L, Universe* U)
62{ 62{
63 ASSERT_L(universe_get(L) == nullptr); 63 ASSERT_L(!U || universe_get(L) == nullptr);
64 STACK_CHECK_START_REL(L, 0); 64 STACK_CHECK_START_REL(L, 0);
65 UNIVERSE_LIGHT_REGKEY.setValue(L, [U](lua_State* L) { U ? lua_pushlightuserdata( L, U) : lua_pushnil( L); }); 65 UNIVERSE_LIGHT_REGKEY.setValue(L, [U](lua_State* L) { U ? lua_pushlightuserdata( L, U) : lua_pushnil( L); });
66 STACK_CHECK( L, 0); 66 STACK_CHECK( L, 0);