VLink  2.0.0
A high-performance communication middleware
task_handle.h
浏览该文件的文档.
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 /**
25  * @file task_handle.h
26  * @brief Observable handle returned by tracked task-posting APIs of @c MessageLoop and @c ThreadPool.
27  *
28  * @details
29  * @c vlink::TaskHandle is a value-typed observer backed by a refcounted shared state
30  * block. Copying or moving a handle is cheap and every copy refers to the same task.
31  * Destroying every handle does @b not cancel the underlying task: the dispatcher retains
32  * its own reference for the duration of the task lifecycle.
33  *
34  * Lifecycle of a tracked task:
35  *
36  * @verbatim
37  * created ---> kQueued -----> kRunning -----> kCompleted (terminal)
38  * | | | \-> kFailed (terminal)
39  * | v v
40  * | kCancelled kCancelled (cancel observed)
41  * | kDropped (terminal)
42  * v
43  * kInvalid / kRejected (terminal)
44  * @endverbatim
45  *
46  * Types defined here:
47  *
48  * | Type | Role |
49  * | --------------------- | --------------------------------------------------------------------- |
50  * | @c TaskExecutionState | Lifecycle stage of a tracked task (queued / running / terminal). |
51  * | @c TaskOverflowPolicy | Per-post override for bounded-queue overflow behaviour. |
52  * | @c TaskDropPolicy | Whether an accepted task may be discarded by drop-oldest paths. |
53  * | @c PostTaskOptions | Aggregate of optional knobs for tracked @c post_task overloads. |
54  * | @c TaskHandle | Observer object handed to the caller. |
55  *
56  * @par Lock ordering
57  * @c TaskHandle's internal state mutex is the innermost mutex in the dispatching layer.
58  * The expected order is
59  * @c MessageLoop::AliveState::mtx -> @c MessageLoop::Impl::mtx -> @c TaskHandle::State::mtx.
60  * @c TaskHandle::cancel() acquires the cancellation source's internal mutex only after
61  * releasing the handle mutex, so the cancellation source is never nested inside any
62  * dispatching-layer mutex. Callbacks added through @c cancellation_token() fire outside
63  * @c TaskHandle::State::mtx, so they may safely re-enter the handle.
64  *
65  * @par Example
66  * @code
67  * vlink::PostTaskOptions opts;
68  * opts.cancellation_token = parent.token();
69  * auto handle = loop.post_task_handle([] { work(); }, opts);
70  *
71  * if (giving_up) {
72  * handle.cancel();
73  * }
74  *
75  * handle.wait();
76  *
77  * if (handle.state() == vlink::TaskExecutionState::kFailed) {
78  * inspect_logs();
79  * }
80  * @endcode
81  *
82  * @note Destroying a @c TaskHandle does not cancel the task; pair with @c cancel() when
83  * cancellation is required.
84  */
85 
86 #pragma once
87 
88 #include <cstdint>
89 #include <memory>
90 
91 #include "./cancellation.h"
92 #include "./functional.h"
93 #include "./macros.h"
94 
95 namespace vlink {
96 
97 class MessageLoop;
98 class ThreadPool;
99 
100 /**
101  * @enum TaskExecutionState
102  * @brief Observable lifecycle stage of a tracked task.
103  *
104  * @details
105  * Non-terminal states (@c kInvalid, @c kQueued, @c kRunning) may transition further;
106  * terminal states (@c kCompleted, @c kCancelled, @c kDropped, @c kRejected, @c kFailed)
107  * are stable and unblock all waiters on @c TaskHandle::wait().
108  */
109 enum class TaskExecutionState : uint8_t {
110  kInvalid = 0, ///< Non-terminal: empty handle or no associated task yet.
111  kQueued, ///< Non-terminal: dispatcher accepted the task but has not yet run it.
112  kRunning, ///< Non-terminal: task callback is currently executing.
113  kCompleted, ///< Terminal: callback returned normally.
114  kCancelled, ///< Terminal: task was cancelled before its callback executed.
115  kDropped, ///< Terminal: task was removed from a bounded queue before execution.
116  kRejected, ///< Terminal: dispatcher refused the submission (quitting / queue full).
117  kFailed, ///< Terminal: task callback raised an exception.
118 };
119 
120 /**
121  * @enum TaskOverflowPolicy
122  * @brief Per-post override for the bounded-queue overflow strategy.
123  *
124  * @details
125  * @c kUseDispatcherStrategy inherits the dispatcher's configured behaviour; the other
126  * values temporarily force @c kReject or @c kBlock for a single submission.
127  */
128 enum class TaskOverflowPolicy : uint8_t {
129  kUseDispatcherStrategy = 0, ///< Inherit the dispatcher-configured overflow strategy.
130  kReject, ///< Return a rejected handle immediately when the queue is full.
131  kBlock, ///< Block until queue space appears or the dispatcher quits.
132 };
133 
134 /**
135  * @enum TaskDropPolicy
136  * @brief Whether an accepted task may later be discarded by drop-oldest overflow paths.
137  *
138  * @details
139  * Even after admission, drop-oldest dispatchers may evict older tasks to make room for
140  * newer work. Mark a task @c kProtected to keep it out of that selection.
141  */
142 enum class TaskDropPolicy : uint8_t {
143  kDroppable = 0, ///< The task is a valid drop candidate.
144  kProtected, ///< The task must not be dropped after admission. (Lock-free dispatchers do not honour this; see
145  ///< @c MessageLoop::post_task documentation.)
146 };
147 
148 /**
149  * @struct PostTaskOptions
150  * @brief Bundle of optional knobs accepted by tracked task-posting APIs.
151  *
152  * @details
153  * Every field has a safe default so a default-constructed @c PostTaskOptions matches the
154  * dispatcher's behaviour for an unprotected, non-cancellable task.
155  */
157  /**
158  * @brief Parent cancellation token that can request abort of this submission.
159  *
160  * @details
161  * An empty token (the default) means only the handle's own
162  * @c cancellation_token() may request cancellation. When the supplied parent is
163  * already cancelled at submission time the returned handle is immediately marked
164  * @c TaskExecutionState::kCancelled. Cancellation observed while the task is queued
165  * causes the task to be skipped on dequeue.
166  */
168 
169  /**
170  * @brief Queue-full strategy applied to this single submission.
171  */
173 
174  /**
175  * @brief Whether the dispatcher is permitted to drop this task to make room for newer work.
176  */
178 };
179 
180 /**
181  * @class TaskHandle
182  * @brief Shared observable handle returned by tracked posting APIs.
183  *
184  * @details
185  * Backed by a @c std::shared_ptr to an internal state block, so copies and moves all
186  * point at the same task. Provides cooperative cancellation, blocking wait on the
187  * terminal state, and a task-local @c CancellationToken that running callbacks can poll.
188  */
190  public:
191  /**
192  * @brief Sentinel timeout value for @c wait() meaning wait indefinitely.
193  */
194  static constexpr int kInfinite{-1};
195 
196  /**
197  * @brief Constructs an invalid handle that references no task.
198  *
199  * @details
200  * @c valid() returns @c false and @c state() returns @c TaskExecutionState::kInvalid.
201  */
202  TaskHandle() noexcept;
203 
204  /**
205  * @brief Destroys the handle reference.
206  *
207  * @note Destruction does not cancel the associated task; the dispatcher retains its own
208  * reference to the state block.
209  */
211 
212  /**
213  * @brief Copy-constructs a handle that shares state with @p other.
214  */
215  TaskHandle(const TaskHandle&) noexcept;
216 
217  /**
218  * @brief Copy-assigns from another handle; both handles share state afterwards.
219  *
220  * @return Reference to @c *this.
221  */
222  TaskHandle& operator=(const TaskHandle&) noexcept;
223 
224  /**
225  * @brief Move-constructs a handle, transferring the shared state from the source.
226  */
227  TaskHandle(TaskHandle&&) noexcept;
228 
229  /**
230  * @brief Move-assigns from another handle, transferring its shared state.
231  *
232  * @return Reference to @c *this.
233  */
234  TaskHandle& operator=(TaskHandle&&) noexcept;
235 
236  /**
237  * @brief Reports whether this handle refers to a live task state block.
238  *
239  * @return @c false when default-constructed or moved-from.
240  */
241  [[nodiscard]] bool valid() const noexcept;
242 
243  /**
244  * @brief Returns the current lifecycle stage of the task.
245  *
246  * @return Latest @c TaskExecutionState; @c kInvalid when the handle is not valid.
247  */
248  [[nodiscard]] TaskExecutionState state() const noexcept;
249 
250  /**
251  * @brief Reports whether the task has reached a terminal state.
252  *
253  * @return @c true once @c state() is one of @c kCompleted, @c kCancelled, @c kDropped,
254  * @c kRejected or @c kFailed.
255  */
256  [[nodiscard]] bool is_done() const noexcept;
257 
258  /**
259  * @brief Returns the task-local cancellation token associated with this handle.
260  *
261  * @details
262  * Running callbacks may poll this token for cooperative abort. Callbacks registered
263  * on the token fire outside the handle mutex, so they may safely re-enter the handle.
264  *
265  * @return Associated token; an empty token when the handle is not valid.
266  */
267  [[nodiscard]] CancellationToken cancellation_token() const noexcept;
268 
269  /**
270  * @brief Requests cooperative cancellation of the task.
271  *
272  * @details
273  * No-op when the handle is invalid or already terminal. When the task has not started
274  * running (@c kInvalid or @c kQueued) this call transitions the state to @c kCancelled;
275  * in any non-terminal case it also flips the cancellation source so running callbacks
276  * polling the token observe the request.
277  *
278  * @return @c true when this call either transitioned the state or flipped the source;
279  * @c false when the handle was invalid or already terminal.
280  */
281  bool cancel() const;
282 
283  /**
284  * @brief Blocks until the task reaches a terminal state or the timeout elapses.
285  *
286  * @details
287  * Never throws on cancellation; inspect @c state() after the call to distinguish the
288  * possible outcomes.
289  *
290  * @param timeout_ms Maximum wait in milliseconds; @c kInfinite waits forever.
291  * @return @c true when a terminal state was reached before the deadline.
292  */
293  bool wait(int timeout_ms = kInfinite) const;
294 
295  private:
296  struct State;
297 
298  explicit TaskHandle(std::shared_ptr<State> state) noexcept;
299 
300  static TaskHandle make_task_handle(const CancellationToken& parent_token = {});
301 
302  static MoveFunction<void()> make_tracked_task(TaskHandle handle, MoveFunction<void()>&& callback);
303 
304  static void mark_task_queued(const TaskHandle& handle);
305 
306  static void mark_task_rejected(const TaskHandle& handle);
307 
308  static bool begin_task_execution(const TaskHandle& handle);
309 
310  static void complete_task_execution(const TaskHandle& handle);
311 
312  static void fail_task_execution(const TaskHandle& handle);
313 
314  static void drop_task_if_queued(const TaskHandle& handle);
315 
316  static bool request_cancel(std::shared_ptr<State> state);
317 
318  std::shared_ptr<State> state_;
319 
320  friend class MessageLoop;
321  friend class ThreadPool;
322  friend class TrackedTask;
323 };
324 
325 } // namespace vlink
Cooperative one-shot cancellation primitives shared by VLink async building blocks.
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81