aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-11 17:24:30 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-11 17:24:30 +0200
commit740b820e429cf7c856f2f305b6fc5b6fd969f3b3 (patch)
treece885dc8200ad8c7a1f6b3506db9ceedc3cc9df7
parent5f5b287b4548cb21fde9ae451392d82e64cc5de7 (diff)
downloadlanes-740b820e429cf7c856f2f305b6fc5b6fd969f3b3.tar.gz
lanes-740b820e429cf7c856f2f305b6fc5b6fd969f3b3.tar.bz2
lanes-740b820e429cf7c856f2f305b6fc5b6fd969f3b3.zip
Move some cancel-related code around
-rw-r--r--src/cancel.cpp113
-rw-r--r--src/cancel.h4
-rw-r--r--src/lane.cpp65
-rw-r--r--src/lane.h10
-rw-r--r--src/universe.cpp4
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//
68LUAG_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_) 83CancelOp 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
142CancelResult 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
165CancelOp 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//
130LUAG_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])
203LUAG_FUNC(thread_cancel) 140LUAG_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
39static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel 39static 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 };
39static constexpr UniqueKey kCachedTostring{ 0xAB5EA23BCEA0C35Cull }; 39static constexpr UniqueKey kCachedTostring{ 0xAB5EA23BCEA0C35Cull };
40 40
41// ################################################################################################# 41// #################################################################################################
42// #################################################################################################
42// ######################################### Lua API ############################################### 43// ######################################### Lua API ###############################################
43// ################################################################################################# 44// #################################################################################################
45// #################################################################################################
44 46
45static LUAG_FUNC(get_debug_threadname) 47static LUAG_FUNC(get_debug_threadname)
46{ 48{
@@ -830,6 +832,69 @@ Lane::~Lane()
830 832
831// ################################################################################################# 833// #################################################################################################
832 834
835CancelResult 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
833void Lane::changeDebugName(int const nameIdx_) 898void Lane::changeDebugName(int const nameIdx_)
834{ 899{
835 int const _nameIdx{ luaG_absindex(L, nameIdx_) }; 900 int const _nameIdx{ luaG_absindex(L, nameIdx_) };
diff --git a/src/lane.h b/src/lane.h
index 6146df8..7c75fe8 100644
--- a/src/lane.h
+++ b/src/lane.h
@@ -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