diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-03-29 16:21:56 +0100 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-03-29 16:21:56 +0100 |
commit | 336935ed4920e1359ab30261a5a028a70d379241 (patch) | |
tree | 147c901be2c28cda3422953d4da6939240696801 /src | |
parent | acb203829bfd12b4cc0c90f82cb70a4d111bce46 (diff) | |
download | lanes-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.cpp | 241 | ||||
-rw-r--r-- | src/lanes_private.h | 5 | ||||
-rw-r--r-- | src/universe.cpp | 2 |
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 | */ |
449 | static int selfdestruct_gc( lua_State* L) | 453 | static 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 | ||
886 | static THREAD_RETURN_T THREAD_CALLCONV lane_main(void* vs) | 892 | static 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 | // |
1047 | LUAG_FUNC( lane_new) | 1060 | LUAG_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 | // |
1283 | LUAG_FUNC(thread_gc) | 1324 | static 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 | ||
61 | void universe_store(lua_State* L, Universe* U) | 61 | void 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); |