VLink  2.0.0
A high-performance communication middleware
multi_loop.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 multi_loop.h
26  * @brief Multi-threaded variant of @c MessageLoop that forwards tasks to an internal @c ThreadPool.
27  *
28  * @details
29  * @c MultiLoop reuses every @c MessageLoop posting API but offloads execution to a pool of
30  * worker threads. The dispatcher thread still dequeues tasks; instead of running each task
31  * inline it hands them to the worker pool through the @c on_task_changed hook. Callers post
32  * via @c post_task / @c exec_task exactly as with a single-threaded loop.
33  *
34  * @par Dispatch behaviour (delegated to the underlying @c ThreadPool)
35  *
36  * Forwarded tasks are submitted to the pool's single shared queue. Whichever worker is
37  * idle dequeues the next task, woken by the pool's condition variable. There is no
38  * round-robin, least-loaded, or hash-based worker selection: cross-worker ordering and
39  * thread affinity are not guaranteed.
40  *
41  * @par Worker diagram
42  *
43  * @verbatim
44  * post_task / exec_task
45  * |
46  * v
47  * +---------------------------------+
48  * | MessageLoop queue |
49  * +----------------+----------------+
50  * |
51  * v
52  * +---------------------------------+
53  * | dispatcher thread |
54  * +-------+--------+--------+-------+
55  * | | |
56  * v v v
57  * +-------+ +-------+ +-------+
58  * | Wkr 1 | | Wkr 2 | | Wkr N |
59  * +-------+ +-------+ +-------+
60  * @endverbatim
61  *
62  * @par Differences vs @c MessageLoop
63  * - Tasks may run concurrently on multiple worker threads; execution order is unspecified.
64  * - @c is_in_same_thread returns @c true for the dispatcher and every worker thread.
65  * - @c on_begin / @c on_end are still invoked once on the dispatcher; they construct and tear
66  * down the internal pool.
67  * - When the pool rejects a forwarded task (for example a zero-worker pool) the base
68  * @c MessageLoop::on_task_changed runs the callback inline on the dispatcher.
69  *
70  * @par Example
71  * @code
72  * vlink::MultiLoop loop(4);
73  * loop.async_run();
74  *
75  * for (int i = 0; i < 100; ++i) {
76  * loop.post_task([i] { process(i); });
77  * }
78  *
79  * loop.wait_for_idle();
80  * loop.quit();
81  * loop.wait_for_quit();
82  * @endcode
83  *
84  * @note Shared state touched inside callbacks must be protected externally. Timers attached to
85  * a @c MultiLoop fire as queue tasks; the dispatcher forwards them to a worker. The
86  * destructor is defaulted; always call @c quit and @c wait_for_quit before destruction.
87  */
88 
89 #pragma once
90 
91 #include <memory>
92 
93 #include "./message_loop.h"
94 
95 namespace vlink {
96 
97 /**
98  * @class MultiLoop
99  * @brief @c MessageLoop subclass that forwards every task to an internal @c ThreadPool.
100  *
101  * @details
102  * Inherits every posting API from @c MessageLoop. The dispatcher pulls tasks from the queue in
103  * FIFO order; the worker pool runs them concurrently so execution order is not guaranteed even
104  * though enqueue order is.
105  */
107  public:
108  /**
109  * @brief Constructs a @c MultiLoop with the default @c kNormalType queue and @p thread_num workers.
110  *
111  * @param thread_num Worker count. Default: @c 4. A zero-worker pool rejects forwarded posts,
112  * so tasks fall back to inline execution on the dispatcher.
113  */
114  explicit MultiLoop(size_t thread_num = 4U);
115 
116  /**
117  * @brief Constructs a @c MultiLoop with a specific queue type.
118  *
119  * @param thread_num Worker count.
120  * @param type Queue implementation type from @c MessageLoop::Type.
121  */
122  explicit MultiLoop(size_t thread_num, Type type);
123 
124  /**
125  * @brief Defaulted destructor.
126  *
127  * @warning Call @c quit and @c wait_for_quit before destruction; the base
128  * @c MessageLoop destructor runs after @c MultiLoop's members are already torn down
129  * and cannot guarantee the worker pool is reset.
130  */
131  ~MultiLoop() override;
132 
133  /**
134  * @brief Reports whether the calling thread belongs to this loop.
135  *
136  * @return @c true on the dispatcher or any worker thread of the internal pool.
137  */
138  [[nodiscard]] bool is_in_same_thread() const override;
139 
140  /**
141  * @brief Waits until both the dispatcher queue and the worker pool reach idle.
142  *
143  * @param ms Maximum wait in milliseconds; @c Timer::kInfinite means no limit.
144  * @param check When @c true, rejects calls from threads owned by this loop.
145  * @return @c true when both queues drained before the timeout.
146  */
147  bool wait_for_idle(int ms = Timer::kInfinite, bool check = true) override;
148 
149  protected:
150  /**
151  * @brief Hook invoked once on the dispatcher when the loop starts; constructs the worker pool.
152  */
153  void on_begin() override;
154 
155  /**
156  * @brief Hook invoked once on the dispatcher when the loop exits; shuts the worker pool down.
157  */
158  void on_end() override;
159 
160  /**
161  * @brief Forwards a task to the internal pool, falling back to the inline base implementation
162  * when the pool rejects the post.
163  *
164  * @param callback Task to dispatch.
165  * @param start_time Millisecond @c steady_clock timestamp captured at enqueue time.
166  */
167  void on_task_changed(Callback&& callback, uint32_t start_time) override;
168 
169  private:
170  struct Impl;
171  std::unique_ptr<Impl> impl_;
172 
174 };
175 
176 } // namespace vlink
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174
Single-threaded task dispatcher with three queue backends, timers and scheduling envelopes.