VLink  2.1.0
A high-performance communication middleware
schedule.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 schedule.h
26  * @brief Fluent task-scheduling wrapper used by @c MessageLoop::exec_task() and family.
27  *
28  * @details
29  * @c vlink::Schedule is a non-instantiable utility struct that wraps a user callable in a
30  * @c Config envelope and produces a @c Status (or @c RetStatus for bool-returning callbacks)
31  * RAII handle. The handle lets callers attach continuation callbacks in a fluent style,
32  * including success/failure branches, exception handling, and scheduling/execution timeout
33  * hooks.
34  *
35  * Categories of options carried by @c Schedule::Config:
36  *
37  * | Field | Purpose |
38  * | ------------------------ | ---------------------------------------------------------------------- |
39  * | @c delay_ms | Wait before posting via a one-shot Timer. |
40  * | @c priority | Dispatch priority for priority-aware message loops. |
41  * | @c schedule_timeout_ms | Maximum queue-wait budget after the delay. Triggered once when the |
42  * | | task is dequeued. Drops the task on expiry. |
43  * | @c execution_timeout_ms | Maximum execution budget per callback in the chain. Reported once |
44  * | | after each callback returns; never interrupts a running callback. |
45  *
46  * Status surface exposed to the caller:
47  *
48  * @verbatim
49  * exec_task() -> Status / RetStatus
50  * +--> on_schedule_timeout(cb)
51  * +--> on_execution_timeout(cb)
52  * +--> on_catch(cb)
53  * +--> [RetStatus] on_then(cb)
54  * +--> [RetStatus] on_else(cb)
55  * @endverbatim
56  *
57  * @note
58  * - @c Status is move-only and may be returned by value while it is being configured.
59  * - The wrapped task is not posted to its dispatcher until the configuration handle is committed
60  * (when it is destroyed, i.e. the end of the fluent expression). This guarantees that every
61  * continuation attached through the chain is registered before the task runs, so a fast
62  * dispatcher can never win a race against the caller and drop a continuation.
63  * - Callers that store the handle instead of consuming it inline must call @c dispatch() to post
64  * the task immediately; otherwise it is posted only when the stored handle is destroyed.
65  * - All continuation callbacks run on the dispatcher thread that ran the wrapped task.
66  *
67  * @par Example
68  * @code
69  * vlink::MessageLoop loop;
70  * loop.async_run();
71  *
72  * loop.exec_task(vlink::Schedule::Config{100},
73  * [] { do_work(); })
74  * .on_execution_timeout([] { VLOG_W("slow"); });
75  *
76  * loop.exec_task(vlink::Schedule::Config{},
77  * []() -> bool { return try_connect(); })
78  * .on_then([] { start_session(); })
79  * .on_else([] { schedule_retry(); });
80  * @endcode
81  */
82 
83 #pragma once
84 
85 #include <atomic>
86 #include <chrono>
87 #include <cstdint>
88 #include <memory>
89 #include <mutex>
90 #include <vector>
91 
92 #include "./functional.h"
93 #include "./macros.h"
94 
95 namespace vlink {
96 
97 class MessageLoop;
98 
99 /**
100  * @struct Schedule
101  * @brief Non-instantiable container for task-scheduling types and the @c process() entry points.
102  *
103  * @details
104  * Provides the @c Config envelope, the @c Status / @c RetStatus handles, and the
105  * @c process / @c process_with_ret static functions used by @c MessageLoop::exec_task().
106  */
107 struct VLINK_EXPORT Schedule final {
108  /**
109  * @brief Callback signature for void tasks and lifecycle hooks.
110  */
111  using Callback = MoveFunction<void()>;
112 
113  /**
114  * @brief Callback signature for tasks that return a boolean indicating success.
115  */
116  using RetCallback = MoveFunction<bool()>;
117 
118  /**
119  * @brief Callback signature for exception handlers attached via @c on_catch().
120  */
121  using CatchCallback = MoveFunction<void(std::exception&)>;
122 
123  /**
124  * @struct Config
125  * @brief Scheduling parameters captured at the call to @c exec_task().
126  *
127  * @details
128  * All fields default to zero, which corresponds to immediate dispatch with no timeouts.
129  */
130  struct VLINK_EXPORT Config final {
131  /**
132  * @brief Constructs a default @c Config with every field zero-initialised.
133  */
135 
136  /**
137  * @brief Constructs a fully populated @c Config.
138  *
139  * @param _delay_ms Delay before posting in milliseconds.
140  * @param _priority Dispatch priority for priority-aware loops.
141  * @param _schedule_timeout_ms Maximum queue-wait budget after the delay.
142  * @param _execution_timeout_ms Maximum execution budget per callback in the chain.
143  */
144  explicit Config(uint32_t _delay_ms, uint16_t _priority = 0, uint32_t _schedule_timeout_ms = 0,
145  uint32_t _execution_timeout_ms = 0);
146 
147  uint32_t delay_ms{0}; ///< Delay before posting; @c 0 posts immediately.
148  uint16_t priority{0}; ///< Dispatch priority hint; higher fires sooner.
149  uint32_t schedule_timeout_ms{0}; ///< Queue-wait budget after the delay; @c 0 disables.
150  uint32_t execution_timeout_ms{0}; ///< Execution budget per callback; @c 0 disables.
151  };
152 
153  /**
154  * @class Status
155  * @brief RAII handle returned by @c exec_task() when the wrapped callback returns @c void.
156  *
157  * @details
158  * Holds a shared reference to the underlying task state. Continuation callbacks may be attached
159  * in any order before the configuration is committed; the wrapped task is not dispatched until
160  * commit, which happens when the originating handle is destroyed (i.e. the end of the fluent
161  * expression) or when @c dispatch() is called explicitly. Every callback attached before commit
162  * is therefore guaranteed to be registered before the task runs; attachments after commit are
163  * ignored. Committing does not cancel the task; it posts it.
164  */
166  public:
167  /**
168  * @brief Constructs a fresh handle backed by a newly allocated task state.
169  */
171 
172  /**
173  * @brief Destroys the handle reference, committing the configuration so the task may dispatch.
174  *
175  * @details
176  * Commits the pending configuration: once the originating handle is gone no further
177  * continuation callbacks can be attached, so the dispatcher is released to run the task
178  * with the full callback chain in place. Does not cancel the task.
179  */
181 
182  Status(const Status&) = delete;
183 
184  Status& operator=(const Status&) = delete;
185 
186  /**
187  * @brief Move-constructs from @p status, transferring its task state.
188  *
189  * @param status Source handle to move from.
190  */
191  Status(Status&& status) noexcept;
192 
193  /**
194  * @brief Move-assigns from @p status, transferring its task state.
195  *
196  * @param status Source handle to move from.
197  * @return Reference to @c *this.
198  */
199  Status& operator=(Status&& status) noexcept;
200 
201  /**
202  * @brief Marks whether the handle's underlying task was successfully posted.
203  *
204  * @param valid @c true once the task is queued.
205  */
206  void set_valid(bool valid);
207 
208  /**
209  * @brief Reports whether the wrapped task was successfully posted.
210  *
211  * @return @c true when the handle refers to a queued task.
212  */
213  [[nodiscard]] bool is_valid() const;
214 
215  /**
216  * @brief Commits the configuration immediately and reports whether the task was posted.
217  *
218  * @details
219  * The task is otherwise posted when the handle is destroyed. A caller that stores the handle
220  * instead of consuming it inline calls @c dispatch() to post it now and learn the result
221  * synchronously. Attach all continuations first; idempotent with the destructor.
222  *
223  * @return @c true when the task was accepted by its dispatcher.
224  */
225  bool dispatch();
226 
227  /**
228  * @brief Installs the callback fired when the task missed its scheduling deadline.
229  *
230  * @details
231  * Triggered once when the task is dequeued and the elapsed time since posting exceeds
232  * @c delay_ms + @c schedule_timeout_ms. Only one schedule-timeout callback may be
233  * registered; late registrations are dropped.
234  *
235  * @param callback Hook invoked on the dispatcher thread.
236  * @return Reference to @c *this for fluent chaining.
237  */
239 
240  /**
241  * @brief Installs the callback fired when a chained callback exceeds @c execution_timeout_ms.
242  *
243  * @details
244  * Triggered after each callback in the chain returns. Only one execution-timeout
245  * callback may be registered; late registrations are dropped. Does not interrupt a
246  * running callback.
247  *
248  * @param callback Hook invoked on the dispatcher thread.
249  * @return Reference to @c *this for fluent chaining.
250  */
252 
253  /**
254  * @brief Installs the callback fired when the task throws a @c std::exception.
255  *
256  * @details
257  * Only @c std::exception-derived failures are caught; other exception types are
258  * allowed to propagate. Only one catch callback may be registered.
259  *
260  * @param callback Hook receiving the caught exception.
261  * @return Reference to @c *this for fluent chaining.
262  */
264 
265  protected:
266  friend Schedule;
267  friend MessageLoop;
268 
269  struct Impl final {
270  std::atomic_bool is_valid{false};
271  std::atomic_bool dispatched{false};
272  std::recursive_mutex mtx;
273  std::chrono::steady_clock::time_point submit_time{std::chrono::steady_clock::now()};
278  std::vector<RetCallback> then_callback_list;
279  };
280 
281  /**
282  * @brief Runs the installed launcher exactly once, posting the wrapped task to its dispatcher.
283  *
284  * @details
285  * Posting is deferred until the handle is committed so every fluent continuation is registered
286  * before the task is enqueued. The launcher lives in the handle (not the shared state), so the
287  * post payload is released with the handle and forms no reference cycle. Idempotent and safe on
288  * a moved-from handle.
289  */
290  void commit() noexcept;
291 
292  std::shared_ptr<Impl> impl_;
293  Callback launcher_;
294  bool committed_{false};
295  };
296 
297  /**
298  * @class RetStatus
299  * @brief RAII handle returned by @c exec_task() when the wrapped callback returns @c bool.
300  *
301  * @details
302  * Extends @c Status with the @c on_then chain and the @c on_else fallback so callers can
303  * express success/failure branches inline.
304  */
305  class VLINK_EXPORT RetStatus final : public Status {
306  public:
307  using Status::Status;
308 
309  /**
310  * @brief Installs the callback fired when the wrapped task returns @c false.
311  *
312  * @details
313  * Only one else callback may be registered; late registrations are dropped.
314  *
315  * @param callback Hook invoked on the dispatcher thread when @c false is returned.
316  * @return Reference to the base @c Status for further chaining.
317  */
318  Status& on_else(Callback&& callback);
319 
320  /**
321  * @brief Appends a continuation that runs only when the previous callback returned @c true.
322  *
323  * @details
324  * Multiple @c on_then callbacks may be chained. Each is invoked in registration order
325  * until one returns @c false, at which point the chain stops and the registered
326  * @c on_else (if any) fires.
327  *
328  * @param callback Continuation taking no arguments and returning @c bool.
329  * @return Reference to @c *this for further chaining.
330  */
332  };
333 
334  Schedule() = delete;
335 
336  Schedule(const Schedule&) = delete;
337 
338  Schedule& operator=(const Schedule&) = delete;
339 
340  Schedule(Schedule&&) = delete;
341 
343 
344  /**
345  * @brief Wraps a void callback in a @c Config envelope and produces the task wrapper for the dispatcher.
346  *
347  * @details
348  * Called internally by @c MessageLoop::exec_task(). Allocates the @c Status state and
349  * fills @p wrapper_callback with the closure that the dispatcher will eventually run.
350  *
351  * @param config Scheduling configuration.
352  * @param callback Void callable to execute.
353  * @param wrapper_callback Out parameter receiving the dispatcher-ready wrapper.
354  * @return Fresh @c Status handle for fluent chaining.
355  */
356  [[nodiscard]] static Status process(const Config& config, Callback&& callback, Callback& wrapper_callback);
357 
358  /**
359  * @brief Wraps a bool-returning callback in a @c Config envelope and produces the task wrapper for the dispatcher.
360  *
361  * @details
362  * Called internally by @c MessageLoop::exec_task(). Allocates the @c RetStatus state and
363  * fills @p wrapper_callback with the closure that the dispatcher will eventually run.
364  *
365  * @param config Scheduling configuration.
366  * @param callback Bool-returning callable to execute.
367  * @param wrapper_callback Out parameter receiving the dispatcher-ready wrapper.
368  * @return Fresh @c RetStatus handle for fluent chaining.
369  */
370  [[nodiscard]] static RetStatus process_with_ret(const Config& config, RetCallback&& callback,
371  Callback& wrapper_callback);
372 
373  private:
374  static RetStatus internal_process_with_ret(const Config& config, RetCallback&& callback, Callback& wrapper_callback);
375 };
376 
377 } // namespace vlink
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