diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-11 17:24:30 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-11 17:24:30 +0200 |
commit | 740b820e429cf7c856f2f305b6fc5b6fd969f3b3 (patch) | |
tree | ce885dc8200ad8c7a1f6b3506db9ceedc3cc9df7 | |
parent | 5f5b287b4548cb21fde9ae451392d82e64cc5de7 (diff) | |
download | lanes-740b820e429cf7c856f2f305b6fc5b6fd969f3b3.tar.gz lanes-740b820e429cf7c856f2f305b6fc5b6fd969f3b3.tar.bz2 lanes-740b820e429cf7c856f2f305b6fc5b6fd969f3b3.zip |
Move some cancel-related code around
-rw-r--r-- | src/cancel.cpp | 113 | ||||
-rw-r--r-- | src/cancel.h | 4 | ||||
-rw-r--r-- | src/lane.cpp | 65 | ||||
-rw-r--r-- | src/lane.h | 10 | ||||
-rw-r--r-- | src/universe.cpp | 4 |
5 files changed, 103 insertions, 93 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp index 4205d1f..a62fcdf 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp | |||
@@ -50,7 +50,7 @@ THE SOFTWARE. | |||
50 | * Returns CANCEL_SOFT/HARD if any locks are to be exited, and 'raise_cancel_error()' called, | 50 | * Returns CANCEL_SOFT/HARD if any locks are to be exited, and 'raise_cancel_error()' called, |
51 | * to make execution of the lane end. | 51 | * to make execution of the lane end. |
52 | */ | 52 | */ |
53 | [[nodiscard]] static inline CancelRequest cancel_test(lua_State* L_) | 53 | [[nodiscard]] CancelRequest cancel_test(lua_State* const L_) |
54 | { | 54 | { |
55 | Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; | 55 | Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; |
56 | // 'lane' is nullptr for the original main state (and no-one can cancel that) | 56 | // 'lane' is nullptr for the original main state (and no-one can cancel that) |
@@ -58,33 +58,6 @@ THE SOFTWARE. | |||
58 | } | 58 | } |
59 | 59 | ||
60 | // ################################################################################################# | 60 | // ################################################################################################# |
61 | |||
62 | //--- | ||
63 | // bool = cancel_test() | ||
64 | // | ||
65 | // Available inside the global namespace of lanes | ||
66 | // returns a boolean saying if a cancel request is pending | ||
67 | // | ||
68 | LUAG_FUNC(cancel_test) | ||
69 | { | ||
70 | CancelRequest _test{ cancel_test(L_) }; | ||
71 | lua_pushboolean(L_, _test != CancelRequest::None); | ||
72 | return 1; | ||
73 | } | ||
74 | |||
75 | // ################################################################################################# | ||
76 | // ################################################################################################# | ||
77 | |||
78 | [[nodiscard]] static void cancel_hook(lua_State* L_, [[maybe_unused]] lua_Debug* ar_) | ||
79 | { | ||
80 | DEBUGSPEW_CODE(DebugSpew(nullptr) << "cancel_hook" << std::endl); | ||
81 | if (cancel_test(L_) != CancelRequest::None) { | ||
82 | lua_sethook(L_, nullptr, 0, 0); | ||
83 | raise_cancel_error(L_); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | // ################################################################################################# | ||
88 | // ################################################################################################# | 61 | // ################################################################################################# |
89 | 62 | ||
90 | //--- | 63 | //--- |
@@ -105,64 +78,9 @@ LUAG_FUNC(cancel_test) | |||
105 | // | 78 | // |
106 | 79 | ||
107 | // ################################################################################################# | 80 | // ################################################################################################# |
108 | |||
109 | [[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) | ||
110 | { | ||
111 | lane_->cancelRequest = CancelRequest::Soft; // it's now signaled to stop | ||
112 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | ||
113 | if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired | ||
114 | std::condition_variable* const _waiting_on{ lane_->waiting_on }; | ||
115 | if (lane_->status == Lane::Waiting && _waiting_on != nullptr) { | ||
116 | _waiting_on->notify_all(); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return lane_->waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout; | ||
121 | } | ||
122 | |||
123 | // ################################################################################################# | 81 | // ################################################################################################# |
124 | 82 | ||
125 | [[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) | 83 | CancelOp WhichCancelOp(std::string_view const& opString_) |
126 | { | ||
127 | lane_->cancelRequest = CancelRequest::Hard; // it's now signaled to stop | ||
128 | // lane_->thread.get_stop_source().request_stop(); | ||
129 | if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired | ||
130 | std::condition_variable* const _waiting_on{ lane_->waiting_on }; | ||
131 | if (lane_->status == Lane::Waiting && _waiting_on != nullptr) { | ||
132 | _waiting_on->notify_all(); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | CancelResult result{ lane_->waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; | ||
137 | return result; | ||
138 | } | ||
139 | |||
140 | // ################################################################################################# | ||
141 | |||
142 | CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) | ||
143 | { | ||
144 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | ||
145 | // We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | ||
146 | if (lane_->status >= Lane::Done) { | ||
147 | // say "ok" by default, including when lane is already done | ||
148 | return CancelResult::Cancelled; | ||
149 | } | ||
150 | |||
151 | // signal the linda the wake up the thread so that it can react to the cancel query | ||
152 | // let us hope we never land here with a pointer on a linda that has been destroyed... | ||
153 | if (op_ == CancelOp::Soft) { | ||
154 | return thread_cancel_soft(lane_, until_, wakeLane_); | ||
155 | } else if (static_cast<int>(op_) > static_cast<int>(CancelOp::Soft)) { | ||
156 | lua_sethook(lane_->L, cancel_hook, static_cast<int>(op_), hookCount_); | ||
157 | } | ||
158 | |||
159 | return thread_cancel_hard(lane_, until_, wakeLane_); | ||
160 | } | ||
161 | |||
162 | // ################################################################################################# | ||
163 | // ################################################################################################# | ||
164 | |||
165 | CancelOp which_cancel_op(std::string_view const& opString_) | ||
166 | { | 84 | { |
167 | CancelOp _op{ CancelOp::Invalid }; | 85 | CancelOp _op{ CancelOp::Invalid }; |
168 | if (opString_ == "hard") { | 86 | if (opString_ == "hard") { |
@@ -183,11 +101,11 @@ CancelOp which_cancel_op(std::string_view const& opString_) | |||
183 | 101 | ||
184 | // ################################################################################################# | 102 | // ################################################################################################# |
185 | 103 | ||
186 | [[nodiscard]] static CancelOp which_cancel_op(lua_State* L_, int idx_) | 104 | [[nodiscard]] static CancelOp WhichCancelOp(lua_State* const L_, int const idx_) |
187 | { | 105 | { |
188 | if (luaG_type(L_, idx_) == LuaType::STRING) { | 106 | if (luaG_type(L_, idx_) == LuaType::STRING) { |
189 | std::string_view const _str{ luaG_tostring(L_, idx_) }; | 107 | std::string_view const _str{ luaG_tostring(L_, idx_) }; |
190 | CancelOp _op{ which_cancel_op(_str) }; | 108 | CancelOp _op{ WhichCancelOp(_str) }; |
191 | lua_remove(L_, idx_); // argument is processed, remove it | 109 | lua_remove(L_, idx_); // argument is processed, remove it |
192 | if (_op == CancelOp::Invalid) { | 110 | if (_op == CancelOp::Invalid) { |
193 | raise_luaL_error(L_, "invalid hook option %s", _str); | 111 | raise_luaL_error(L_, "invalid hook option %s", _str); |
@@ -198,12 +116,31 @@ CancelOp which_cancel_op(std::string_view const& opString_) | |||
198 | } | 116 | } |
199 | 117 | ||
200 | // ################################################################################################# | 118 | // ################################################################################################# |
119 | // ################################################################################################# | ||
120 | // ######################################### Lua API ############################################### | ||
121 | // ################################################################################################# | ||
122 | // ################################################################################################# | ||
123 | |||
124 | //--- | ||
125 | // bool = cancel_test() | ||
126 | // | ||
127 | // Available inside the global namespace of a lane | ||
128 | // returns a boolean saying if a cancel request is pending | ||
129 | // | ||
130 | LUAG_FUNC(cancel_test) | ||
131 | { | ||
132 | CancelRequest _test{ cancel_test(L_) }; | ||
133 | lua_pushboolean(L_, _test != CancelRequest::None); | ||
134 | return 1; | ||
135 | } | ||
136 | |||
137 | // ################################################################################################# | ||
201 | 138 | ||
202 | // bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lane]) | 139 | // bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lane]) |
203 | LUAG_FUNC(thread_cancel) | 140 | LUAG_FUNC(thread_cancel) |
204 | { | 141 | { |
205 | Lane* const _lane{ ToLane(L_, 1) }; | 142 | Lane* const _lane{ ToLane(L_, 1) }; |
206 | CancelOp const _op{ which_cancel_op(L_, 2) }; // this removes the op string from the stack | 143 | CancelOp const _op{ WhichCancelOp(L_, 2) }; // this removes the op string from the stack |
207 | 144 | ||
208 | int _hook_count{ 0 }; | 145 | int _hook_count{ 0 }; |
209 | if (static_cast<int>(_op) > static_cast<int>(CancelOp::Soft)) { // hook is requested | 146 | if (static_cast<int>(_op) > static_cast<int>(CancelOp::Soft)) { // hook is requested |
@@ -237,7 +174,7 @@ LUAG_FUNC(thread_cancel) | |||
237 | lua_remove(L_, 2); // argument is processed, remove it | 174 | lua_remove(L_, 2); // argument is processed, remove it |
238 | } | 175 | } |
239 | STACK_CHECK_START_REL(L_, 0); | 176 | STACK_CHECK_START_REL(L_, 0); |
240 | switch (thread_cancel(_lane, _op, _hook_count, _until, _wake_lane)) { | 177 | switch (_lane->cancel(_op, _hook_count, _until, _wake_lane)) { |
241 | default: // should never happen unless we added a case and forgot to handle it | 178 | default: // should never happen unless we added a case and forgot to handle it |
242 | LUA_ASSERT(L_, false); | 179 | LUA_ASSERT(L_, false); |
243 | break; | 180 | break; |
diff --git a/src/cancel.h b/src/cancel.h index 3304e0d..0e4e6d4 100644 --- a/src/cancel.h +++ b/src/cancel.h | |||
@@ -38,8 +38,8 @@ enum class CancelOp | |||
38 | // xxh64 of string "kCancelError" generated at https://www.pelock.com/products/hash-calculator | 38 | // xxh64 of string "kCancelError" generated at https://www.pelock.com/products/hash-calculator |
39 | static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel | 39 | static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel |
40 | 40 | ||
41 | [[nodiscard]] CancelOp which_cancel_op(std::string_view const& opString_); | 41 | [[nodiscard]] CancelRequest cancel_test(lua_State* L_); |
42 | [[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); | 42 | [[nodiscard]] CancelOp WhichCancelOp(std::string_view const& opString_); |
43 | 43 | ||
44 | [[noreturn]] static inline void raise_cancel_error(lua_State* L_) | 44 | [[noreturn]] static inline void raise_cancel_error(lua_State* L_) |
45 | { | 45 | { |
diff --git a/src/lane.cpp b/src/lane.cpp index 5e018a4..15499fe 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
@@ -39,8 +39,10 @@ static constexpr UniqueKey kCachedError{ 0xD6F35DD608D0A203ull }; | |||
39 | static constexpr UniqueKey kCachedTostring{ 0xAB5EA23BCEA0C35Cull }; | 39 | static constexpr UniqueKey kCachedTostring{ 0xAB5EA23BCEA0C35Cull }; |
40 | 40 | ||
41 | // ################################################################################################# | 41 | // ################################################################################################# |
42 | // ################################################################################################# | ||
42 | // ######################################### Lua API ############################################### | 43 | // ######################################### Lua API ############################################### |
43 | // ################################################################################################# | 44 | // ################################################################################################# |
45 | // ################################################################################################# | ||
44 | 46 | ||
45 | static LUAG_FUNC(get_debug_threadname) | 47 | static LUAG_FUNC(get_debug_threadname) |
46 | { | 48 | { |
@@ -830,6 +832,69 @@ Lane::~Lane() | |||
830 | 832 | ||
831 | // ################################################################################################# | 833 | // ################################################################################################# |
832 | 834 | ||
835 | CancelResult Lane::cancel(CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) | ||
836 | { | ||
837 | auto _cancel_hook = +[](lua_State* const L_, [[maybe_unused]] lua_Debug* const ar_) { | ||
838 | DEBUGSPEW_CODE(DebugSpew(nullptr) << "cancel_hook" << std::endl); | ||
839 | if (cancel_test(L_) != CancelRequest::None) { | ||
840 | lua_sethook(L_, nullptr, 0, 0); | ||
841 | raise_cancel_error(L_); | ||
842 | } | ||
843 | }; | ||
844 | |||
845 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | ||
846 | // We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | ||
847 | if (status >= Lane::Done) { | ||
848 | // say "ok" by default, including when lane is already done | ||
849 | return CancelResult::Cancelled; | ||
850 | } | ||
851 | |||
852 | // signal the linda the wake up the thread so that it can react to the cancel query | ||
853 | // let us hope we never land here with a pointer on a linda that has been destroyed... | ||
854 | if (op_ == CancelOp::Soft) { | ||
855 | return cancelSoft(until_, wakeLane_); | ||
856 | } else if (static_cast<int>(op_) > static_cast<int>(CancelOp::Soft)) { | ||
857 | lua_sethook(L, _cancel_hook, static_cast<int>(op_), hookCount_); | ||
858 | } | ||
859 | |||
860 | return cancelHard(until_, wakeLane_); | ||
861 | } | ||
862 | |||
863 | // ################################################################################################# | ||
864 | |||
865 | [[nodiscard]] CancelResult Lane::cancelHard(std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) | ||
866 | { | ||
867 | cancelRequest = CancelRequest::Hard; // it's now signaled to stop | ||
868 | // lane_->thread.get_stop_source().request_stop(); | ||
869 | if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired | ||
870 | std::condition_variable* const _waiting_on{ waiting_on }; | ||
871 | if (status == Lane::Waiting && _waiting_on != nullptr) { | ||
872 | _waiting_on->notify_all(); | ||
873 | } | ||
874 | } | ||
875 | |||
876 | CancelResult result{ waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; | ||
877 | return result; | ||
878 | } | ||
879 | |||
880 | // ################################################################################################# | ||
881 | |||
882 | [[nodiscard]] CancelResult Lane::cancelSoft(std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) | ||
883 | { | ||
884 | cancelRequest = CancelRequest::Soft; // it's now signaled to stop | ||
885 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | ||
886 | if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired | ||
887 | std::condition_variable* const _waiting_on{ waiting_on }; | ||
888 | if (status == Lane::Waiting && _waiting_on != nullptr) { | ||
889 | _waiting_on->notify_all(); | ||
890 | } | ||
891 | } | ||
892 | |||
893 | return waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout; | ||
894 | } | ||
895 | |||
896 | // ################################################################################################# | ||
897 | |||
833 | void Lane::changeDebugName(int const nameIdx_) | 898 | void Lane::changeDebugName(int const nameIdx_) |
834 | { | 899 | { |
835 | int const _nameIdx{ luaG_absindex(L, nameIdx_) }; | 900 | int const _nameIdx{ luaG_absindex(L, nameIdx_) }; |
@@ -120,6 +120,14 @@ class Lane | |||
120 | Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_); | 120 | Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_); |
121 | ~Lane(); | 121 | ~Lane(); |
122 | 122 | ||
123 | private: | ||
124 | |||
125 | [[nodiscard]] CancelResult cancelHard(std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); | ||
126 | [[nodiscard]] CancelResult cancelSoft(std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); | ||
127 | |||
128 | public: | ||
129 | |||
130 | CancelResult cancel(CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); | ||
123 | void changeDebugName(int const nameIdx_); | 131 | void changeDebugName(int const nameIdx_); |
124 | void close() { lua_State* _L{ L }; L = nullptr; lua_close(_L); } | 132 | void close() { lua_State* _L{ L }; L = nullptr; lua_close(_L); } |
125 | [[nodiscard]] std::string_view errorTraceLevelString() const; | 133 | [[nodiscard]] std::string_view errorTraceLevelString() const; |
@@ -137,7 +145,7 @@ class Lane | |||
137 | 145 | ||
138 | // To allow free-running threads (longer lifespan than the handle's) | 146 | // To allow free-running threads (longer lifespan than the handle's) |
139 | // 'Lane' are malloc/free'd and the handle only carries a pointer. | 147 | // 'Lane' are malloc/free'd and the handle only carries a pointer. |
140 | // This is not deep userdata since the handle's not portable among lanes. | 148 | // This is not deep userdata since the handle is not portable among lanes. |
141 | // | 149 | // |
142 | [[nodiscard]] inline Lane* ToLane(lua_State* L_, int i_) | 150 | [[nodiscard]] inline Lane* ToLane(lua_State* L_, int i_) |
143 | { | 151 | { |
diff --git a/src/universe.cpp b/src/universe.cpp index 82522f1..c95884f 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -256,7 +256,7 @@ void Universe::terminateFreeRunningLanes(lua_State* const L_, lua_Duration const | |||
256 | // if waiting on a linda, they will raise a cancel_error. | 256 | // if waiting on a linda, they will raise a cancel_error. |
257 | // if a cancellation hook is desired, it will be installed to try to raise an error | 257 | // if a cancellation hook is desired, it will be installed to try to raise an error |
258 | if (_lane->thread.joinable()) { | 258 | if (_lane->thread.joinable()) { |
259 | std::ignore = thread_cancel(_lane, op_, 1, std::chrono::steady_clock::now() + 1us, true); | 259 | std::ignore = _lane->cancel(op_, 1, std::chrono::steady_clock::now() + 1us, true); |
260 | } | 260 | } |
261 | _lane = _lane->selfdestruct_next; | 261 | _lane = _lane->selfdestruct_next; |
262 | } | 262 | } |
@@ -316,7 +316,7 @@ LUAG_FUNC(universe_gc) | |||
316 | std::string_view const _op_string{ luaG_tostring(L_, lua_upvalueindex(2)) }; | 316 | std::string_view const _op_string{ luaG_tostring(L_, lua_upvalueindex(2)) }; |
317 | STACK_CHECK_START_ABS(L_, 1); | 317 | STACK_CHECK_START_ABS(L_, 1); |
318 | Universe* const _U{ luaG_tofulluserdata<Universe>(L_, 1) }; // L_: U | 318 | Universe* const _U{ luaG_tofulluserdata<Universe>(L_, 1) }; // L_: U |
319 | _U->terminateFreeRunningLanes(L_, _shutdown_timeout, which_cancel_op(_op_string)); | 319 | _U->terminateFreeRunningLanes(L_, _shutdown_timeout, WhichCancelOp(_op_string)); |
320 | 320 | ||
321 | // invoke the function installed by lanes.finally() | 321 | // invoke the function installed by lanes.finally() |
322 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil | 322 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil |