diff options
Diffstat (limited to 'src')
| -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 |
