VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
thread_pool.h
Go to the documentation of this file.
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 thread_pool.h
26 * @brief General-purpose thread pool for parallel task execution.
27 *
28 * @details
29 * @c ThreadPool maintains a fixed number of worker threads that dequeue and execute
30 * tasks posted via @c post_task() or @c invoke_task(). Unlike @c MessageLoop, there
31 * is no timer support or loop lifecycle; the pool is started on construction and shut
32 * down with @c shutdown().
33 *
34 * Queue types:
35 *
36 * | Type | Queue implementation | Notes |
37 * | ----------------- | ------------------------------ | ------------------------------- |
38 * | @c kNormalType | Mutex-protected std::queue | Default |
39 * | @c kLockfreeType | MpmcQueue (lock-free MPMC) | Lower overhead under contention |
40 *
41 * Idle strategies (same semantics as @c MessageLoop::Strategy).
42 *
43 * @note
44 * - Tasks may execute concurrently; shared state must be protected externally.
45 * - @c invoke_task() returns a @c std::future. Blocking on the future from a thread
46 * pool worker will deadlock if all workers are busy.
47 * - @c is_in_work_thread() can be used to detect reentrant calls.
48 *
49 * @par Example
50 * @code
51 * vlink::ThreadPool pool(8);
52 * pool.post_task([] { heavy_work(); });
53 *
54 * auto fut = pool.invoke_task([]() -> int { return compute(); });
55 * int result = fut.get();
56 *
57 * pool.shutdown();
58 * @endcode
59 */
60
61#pragma once
62
63#include <functional>
64#include <future>
65#include <memory>
66#include <string>
67#include <utility>
68
69#include "./macros.h"
70
71namespace vlink {
72
73/**
74 * @class ThreadPool
75 * @brief Fixed-size thread pool for parallel task execution.
76 *
77 * @details
78 * Worker threads are created on construction and destroyed on @c shutdown() or destruction.
79 */
81 public:
82 /**
83 * @brief Callback type for tasks submitted to the pool.
84 */
85 using Callback = std::function<void()>;
86
87 /**
88 * @brief Queue implementation type.
89 */
90 enum Type : uint8_t {
91 kNormalType = 0, ///< Mutex-protected FIFO queue (default)
92 kLockfreeType = 1, ///< Lock-free MPMC queue
93 };
94
95 /**
96 * @brief Idle strategy controlling CPU and wake-up latency trade-offs.
97 */
98 enum Strategy : uint8_t {
99 kOptimizationStrategy = 0, ///< Balance latency and CPU via yield
100 kPopStrategy = 1, ///< Busy-poll (lowest latency, highest CPU)
101 kBlockStrategy = 2, ///< Block on condition variable (lowest CPU)
102 };
103
104 /**
105 * @brief Constructs a @c ThreadPool with @p thread_count worker threads and @c kNormalType queue.
106 *
107 * @param thread_count Number of worker threads. Default: 4.
108 */
109 explicit ThreadPool(size_t thread_count = 4U);
110
111 /**
112 * @brief Constructs a @c ThreadPool with the specified thread count and queue type.
113 *
114 * @param thread_count Number of worker threads.
115 * @param type Queue implementation type.
116 */
117 explicit ThreadPool(size_t thread_count, Type type);
118
119 /**
120 * @brief Destructor. Calls @c shutdown() and joins all worker threads.
121 */
122 virtual ~ThreadPool();
123
124 /**
125 * @brief Sets a human-readable name for the pool (and its worker threads).
126 *
127 * @param name Name string.
128 */
129 void set_name(const std::string& name);
130
131 /**
132 * @brief Returns the name set via @c set_name().
133 *
134 * @return Reference to the name string.
135 */
136 [[nodiscard]] const std::string& get_name() const;
137
138 /**
139 * @brief Returns the queue type this pool was constructed with.
140 *
141 * @return Queue type.
142 */
143 [[nodiscard]] Type get_type() const;
144
145 /**
146 * @brief Returns the current idle strategy.
147 *
148 * @return Current strategy.
149 */
150 [[nodiscard]] Strategy get_strategy() const;
151
152 /**
153 * @brief Changes the idle strategy.
154 *
155 * @param strategy New strategy.
156 */
157 void set_strategy(Strategy strategy);
158
159 /**
160 * @brief Signals all workers to finish current tasks and exit.
161 *
162 * @details
163 * Waits for all worker threads to join before returning.
164 * After @c shutdown(), the pool can no longer accept tasks.
165 *
166 * @return @c true on success.
167 */
168 bool shutdown();
169
170 /**
171 * @brief Posts a task to the queue for execution by a worker thread.
172 *
173 * @details
174 * Thread-safe. Returns @c false if the pool is shutting down or the queue is full
175 * (@c get_max_task_count()).
176 *
177 * @param callback Task to execute.
178 * @return @c true if enqueued successfully.
179 */
180 bool post_task(Callback&& callback);
181
182 /**
183 * @brief Returns the number of tasks currently in the queue.
184 *
185 * @return Pending task count.
186 */
187 [[nodiscard]] size_t get_task_count() const;
188
189 /**
190 * @brief Returns @c true if the calling thread is a worker thread of this pool.
191 *
192 * @return @c true if called from a pool worker.
193 */
194 [[nodiscard]] bool is_in_work_thread() const;
195
196 /**
197 * @brief Returns the maximum queue depth.
198 *
199 * @return Maximum number of tasks that can be queued simultaneously.
200 */
201 [[nodiscard]] virtual size_t get_max_task_count() const;
202
203 /**
204 * @brief Dispatches a callable to a worker thread and returns a @c std::future for the result.
205 *
206 * @details
207 * Thread-safe. The future becomes ready after the callable is executed by a worker.
208 *
209 * @warning Do not block on the future from a pool worker thread if all workers are busy;
210 * this will deadlock.
211 *
212 * @tparam FunctionT Callable type.
213 * @tparam ArgsT Argument types.
214 * @tparam ResultT Return type (deduced).
215 * @param function Callable to dispatch.
216 * @param args Arguments forwarded to the callable.
217 * @return @c std::future<ResultT> that becomes ready when the task completes.
218 */
219 template <class FunctionT, class... ArgsT, typename ResultT = std::invoke_result_t<FunctionT, ArgsT...>>
220 [[nodiscard]] std::future<ResultT> invoke_task(FunctionT&& function, ArgsT&&... args);
221
222 private:
223 void init();
224
225 std::unique_ptr<struct ThreadPoolImpl> impl_;
226
228};
229
230////////////////////////////////////////////////////////////////
231/// Details
232////////////////////////////////////////////////////////////////
233
234template <class FunctionT, class... ArgsT, typename ResultT>
235inline std::future<ResultT> ThreadPool::invoke_task(FunctionT&& function, ArgsT&&... args) {
236 auto task = std::make_shared<std::packaged_task<ResultT()>>(
237 std::bind(std::forward<FunctionT>(function), std::forward<ArgsT>(args)...)); // NOLINT(modernize-avoid-bind)
238
239 std::future<ResultT> res = task->get_future();
240
241 std::invoke(&ThreadPool::post_task, this, [task]() { (*task.get())(); });
242
243 return res;
244}
245
246} // namespace vlink
Platform-independent macro definitions for the VLink library.
#define VLINK_EXPORT
Definition macros.h:85
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition macros.h:184