Branch data Line data Source code
1 : : /* 2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved. 3 : : * Author: Thun Lu <thun.lu@zohomail.cn> 4 : : * Repo: https://github.com/thun-res/vlink 5 : : * _ __ __ _ __ 6 : : * | | / / / / (_) ____ / /__ 7 : : * | | / / / / / / / __ \ / //_/ 8 : : * | |/ / / /___ / / / / / / / ,< 9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_| 10 : : * 11 : : * Licensed under the Apache License, Version 2.0 (the "License"); 12 : : * you may not use this file except in compliance with the License. 13 : : * You may obtain a copy of the License at 14 : : * 15 : : * http://www.apache.org/licenses/LICENSE-2.0 16 : : * 17 : : * Unless required by applicable law or agreed to in writing, software 18 : : * distributed under the License is distributed on an "AS IS" BASIS, 19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 : : * See the License for the specific language governing permissions and 21 : : * limitations under the License. 22 : : */ 23 : : 24 : : #include "./base/task_handle.h" 25 : : 26 : : #include <chrono> 27 : : #include <memory> 28 : : #include <mutex> 29 : : #include <utility> 30 : : 31 : : #include "./base/condition_variable.h" 32 : : #include "./base/memory_resource.h" 33 : : 34 : : namespace vlink { 35 : : 36 : 117 : [[maybe_unused]] static bool is_terminal(TaskExecutionState state) noexcept { 37 [ + + + + ]: 85 : return state == TaskExecutionState::kCompleted || state == TaskExecutionState::kCancelled || 38 [ + + + + : 202 : state == TaskExecutionState::kDropped || state == TaskExecutionState::kRejected || + + ] 39 : 117 : state == TaskExecutionState::kFailed; 40 : : } 41 : : 42 : : template <typename StateT> 43 : 61 : [[maybe_unused]] static void release_parent_registration(StateT& state, 44 : : CancellationRegistration& registration) noexcept { 45 : 61 : registration = std::move(state.parent_registration); 46 : 61 : } 47 : : 48 : : // TaskHandle::State 49 : : struct TaskHandle::State final { 50 : : mutable std::mutex mtx; 51 : : ConditionVariable cv; 52 : : TaskExecutionState state{TaskExecutionState::kQueued}; 53 : : CancellationSource cancellation_source; 54 : : CancellationRegistration parent_registration; 55 : : }; 56 : : 57 : : // TrackedTask 58 : : class TrackedTask final { 59 : : public: 60 : 56 : TrackedTask(TaskHandle handle, MoveFunction<void()>&& callback) noexcept 61 : 56 : : handle_(std::move(handle)), callback_(std::move(callback)) {} 62 : : 63 : 56 : TrackedTask(TrackedTask&& other) noexcept 64 : 56 : : handle_(std::move(other.handle_)), callback_(std::move(other.callback_)), armed_(other.armed_) { 65 : 56 : other.armed_ = false; 66 : 56 : } 67 : : 68 : : TrackedTask& operator=(TrackedTask&& other) noexcept { 69 : : if VUNLIKELY (this == &other) { 70 : : return *this; 71 : : } 72 : : 73 : : if VUNLIKELY (armed_) { 74 : : TaskHandle::drop_task_if_queued(handle_); 75 : : } 76 : : 77 : : handle_ = std::move(other.handle_); 78 : : callback_ = std::move(other.callback_); 79 : : armed_ = other.armed_; 80 : : 81 : : other.armed_ = false; 82 : : 83 : : return *this; 84 : : } 85 : : 86 : 112 : ~TrackedTask() { 87 [ + + ]: 112 : if VUNLIKELY (armed_) { 88 : 21 : TaskHandle::drop_task_if_queued(handle_); 89 : : } 90 : 112 : } 91 : : 92 : 35 : void operator()() { 93 : 35 : armed_ = false; 94 : : 95 [ + + ]: 35 : if VUNLIKELY (!TaskHandle::begin_task_execution(handle_)) { 96 : 4 : return; 97 : : } 98 : : 99 : : try { 100 [ + - ]: 31 : if VLIKELY (callback_) { 101 [ + + ]: 31 : callback_(); 102 : : } 103 : : 104 [ + - ]: 27 : TaskHandle::complete_task_execution(handle_); 105 : 4 : } catch (...) { 106 [ + - ]: 4 : TaskHandle::fail_task_execution(handle_); 107 : 4 : } 108 : : } 109 : : 110 : : private: 111 : : TaskHandle handle_; 112 : : MoveFunction<void()> callback_; 113 : : bool armed_{true}; 114 : : 115 : : VLINK_DISALLOW_COPY_AND_ASSIGN(TrackedTask) 116 : : }; 117 : : 118 : : // TaskHandle 119 : 6 : TaskHandle::TaskHandle() noexcept = default; 120 : : 121 : 307 : TaskHandle::~TaskHandle() = default; 122 : : 123 : 58 : TaskHandle::TaskHandle(const TaskHandle&) noexcept = default; 124 : : 125 : 2 : TaskHandle& TaskHandle::operator=(const TaskHandle&) noexcept = default; 126 : : 127 : 182 : TaskHandle::TaskHandle(TaskHandle&&) noexcept = default; 128 : : 129 : 2 : TaskHandle& TaskHandle::operator=(TaskHandle&&) noexcept = default; 130 : : 131 : 12 : bool TaskHandle::valid() const noexcept { return state_ != nullptr; } 132 : : 133 : 343 : TaskExecutionState TaskHandle::state() const noexcept { 134 [ + + ]: 343 : if VUNLIKELY (!state_) { 135 : 7 : return TaskExecutionState::kInvalid; 136 : : } 137 : : 138 : 336 : std::lock_guard lock(state_->mtx); 139 : : 140 : 336 : return state_->state; 141 : 336 : } 142 : : 143 : 28 : bool TaskHandle::is_done() const noexcept { return is_terminal(state()); } 144 : : 145 : 3 : CancellationToken TaskHandle::cancellation_token() const noexcept { 146 [ + + ]: 3 : if VUNLIKELY (!state_) { 147 : 1 : return {}; 148 : : } 149 : : 150 : 2 : return state_->cancellation_source.token(); 151 : : } 152 : : 153 [ + - ]: 9 : bool TaskHandle::cancel() const { return request_cancel(state_); } 154 : : 155 : 46 : bool TaskHandle::wait(int timeout_ms) const { 156 [ + + ]: 46 : if VUNLIKELY (!state_) { 157 : 4 : return false; 158 : : } 159 : : 160 [ + - ]: 42 : std::unique_lock lock(state_->mtx); 161 : : 162 : 65 : auto predicate = [this] { return is_terminal(state_->state); }; 163 : : 164 [ + + ]: 42 : if (timeout_ms < 0) { 165 : 2 : state_->cv.wait(lock, predicate); 166 : : 167 : 2 : return true; 168 : : } 169 : : 170 [ + - ]: 40 : return state_->cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), predicate); 171 : 42 : } 172 : : 173 : 61 : TaskHandle::TaskHandle(std::shared_ptr<State> state) noexcept : state_(std::move(state)) {} 174 : : 175 : 61 : TaskHandle TaskHandle::make_task_handle(const CancellationToken& parent_token) { 176 [ + - ]: 61 : auto state = MemoryResource::make_shared<TaskHandle::State>(); 177 : : 178 [ + + ]: 61 : if VLIKELY (parent_token.valid()) { 179 : 6 : std::weak_ptr<TaskHandle::State> weak_state = state; 180 : : 181 : 6 : auto registration = parent_token.register_callback([weak_state]() { 182 : 6 : auto locked = weak_state.lock(); 183 : : 184 [ + - ]: 6 : if VLIKELY (locked) { 185 [ + - ]: 6 : (void)TaskHandle::request_cancel(std::move(locked)); 186 : : } 187 [ + - + - ]: 18 : }); 188 : : 189 : : { 190 [ + - ]: 6 : std::lock_guard lock(state->mtx); 191 : : 192 : 6 : state->parent_registration = std::move(registration); 193 : 6 : } 194 : : 195 [ + + ]: 6 : if VUNLIKELY (parent_token.is_cancellation_requested()) { 196 [ + - ]: 3 : (void)TaskHandle::request_cancel(state); 197 : : } 198 : 6 : } 199 : : 200 : 122 : return TaskHandle{std::move(state)}; 201 : 61 : } 202 : : 203 : 56 : MoveFunction<void()> TaskHandle::make_tracked_task(TaskHandle handle, MoveFunction<void()>&& callback) { 204 [ + - ]: 112 : return TrackedTask{std::move(handle), std::move(callback)}; 205 : : } 206 : : 207 : 9 : void TaskHandle::mark_task_rejected(const TaskHandle& handle) { 208 [ - + ]: 9 : if VUNLIKELY (!handle.state_) { 209 : 0 : return; 210 : : } 211 : : 212 : 9 : CancellationRegistration parent_registration; 213 : : 214 : : { 215 [ + - ]: 9 : std::lock_guard lock(handle.state_->mtx); 216 : : 217 [ + - ]: 9 : if VLIKELY (!is_terminal(handle.state_->state)) { 218 : 9 : handle.state_->state = TaskExecutionState::kRejected; 219 : : 220 : 9 : release_parent_registration(*handle.state_, parent_registration); 221 : : } 222 : 9 : } 223 : : 224 : 9 : handle.state_->cv.notify_all(); 225 : 9 : } 226 : : 227 : 35 : bool TaskHandle::begin_task_execution(const TaskHandle& handle) { 228 [ - + ]: 35 : if VUNLIKELY (!handle.state_) { 229 : 0 : return false; 230 : : } 231 : : 232 [ + - ]: 35 : std::lock_guard lock(handle.state_->mtx); 233 : : 234 [ + + ]: 35 : if VUNLIKELY (handle.state_->state != TaskExecutionState::kQueued) { 235 : 4 : return false; 236 : : } 237 : : 238 [ - + ]: 31 : if VUNLIKELY (handle.state_->cancellation_source.is_cancellation_requested()) { 239 : 0 : handle.state_->state = TaskExecutionState::kCancelled; 240 : : 241 : 0 : handle.state_->cv.notify_all(); 242 : : 243 : 0 : return false; 244 : : } 245 : : 246 : 31 : handle.state_->state = TaskExecutionState::kRunning; 247 : : 248 : 31 : handle.state_->cv.notify_all(); 249 : : 250 : 31 : return true; 251 : 35 : } 252 : : 253 : 27 : void TaskHandle::complete_task_execution(const TaskHandle& handle) { 254 [ - + ]: 27 : if VUNLIKELY (!handle.state_) { 255 : 0 : return; 256 : : } 257 : : 258 : 27 : CancellationRegistration parent_registration; 259 : : 260 : : { 261 [ + - ]: 27 : std::lock_guard lock(handle.state_->mtx); 262 : : 263 [ + - ]: 27 : if VLIKELY (handle.state_->state == TaskExecutionState::kRunning) { 264 : 27 : handle.state_->state = TaskExecutionState::kCompleted; 265 : : 266 : 27 : release_parent_registration(*handle.state_, parent_registration); 267 : : } 268 : 27 : } 269 : : 270 : 27 : handle.state_->cv.notify_all(); 271 : 27 : } 272 : : 273 : 4 : void TaskHandle::fail_task_execution(const TaskHandle& handle) { 274 [ - + ]: 4 : if VUNLIKELY (!handle.state_) { 275 : 0 : return; 276 : : } 277 : : 278 : 4 : CancellationRegistration parent_registration; 279 : : 280 : : { 281 [ + - ]: 4 : std::lock_guard lock(handle.state_->mtx); 282 : : 283 [ + - ]: 4 : if VLIKELY (handle.state_->state == TaskExecutionState::kRunning) { 284 : 4 : handle.state_->state = TaskExecutionState::kFailed; 285 : : 286 : 4 : release_parent_registration(*handle.state_, parent_registration); 287 : : } 288 : 4 : } 289 : : 290 : 4 : handle.state_->cv.notify_all(); 291 : 4 : } 292 : : 293 : 21 : void TaskHandle::drop_task_if_queued(const TaskHandle& handle) { 294 [ - + ]: 21 : if VUNLIKELY (!handle.state_) { 295 : 0 : return; 296 : : } 297 : : 298 : 21 : CancellationRegistration parent_registration; 299 : : 300 : : { 301 [ + - ]: 21 : std::lock_guard lock(handle.state_->mtx); 302 : : 303 [ + + ]: 21 : if VLIKELY (handle.state_->state == TaskExecutionState::kQueued) { 304 : 12 : handle.state_->state = TaskExecutionState::kDropped; 305 : : 306 : 12 : release_parent_registration(*handle.state_, parent_registration); 307 : : } 308 : 21 : } 309 : : 310 : 21 : handle.state_->cv.notify_all(); 311 : 21 : } 312 : : 313 : 18 : bool TaskHandle::request_cancel(std::shared_ptr<State> state) { 314 [ + + ]: 18 : if VUNLIKELY (!state) { 315 : 3 : return false; 316 : : } 317 : : 318 : 15 : bool changed = false; 319 : : 320 : 15 : CancellationRegistration parent_registration; 321 : : 322 : : { 323 [ + - ]: 15 : std::lock_guard lock(state->mtx); 324 : : 325 [ + + ]: 15 : if VUNLIKELY (is_terminal(state->state)) { 326 : 5 : return false; 327 : : } 328 : : 329 [ + + ]: 10 : if VLIKELY (state->state == TaskExecutionState::kQueued) { 330 : 9 : state->state = TaskExecutionState::kCancelled; 331 : : 332 : 9 : release_parent_registration(*state, parent_registration); 333 : : 334 : 9 : changed = true; 335 : : } 336 [ + + ]: 15 : } 337 : : 338 [ + + ]: 10 : if VLIKELY (changed) { 339 : 9 : state->cv.notify_all(); 340 : : } 341 : : 342 [ + - - + : 10 : changed = state->cancellation_source.request_cancel() || changed; - - ] 343 : : 344 : 10 : return changed; 345 : 15 : } 346 : : 347 : : } // namespace vlink