Dispatch Queue
Dispatch Queue / Thread Pool implementation for C++11 with built-in C++20 coroutine support
 
Loading...
Searching...
No Matches
dispatch_queue.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <utility>
5
6#include "detail/function_result.hpp"
7#include "detail/pending_task_queue.hpp"
8#include "detail/promise.hpp"
9#include "detail/ranges.hpp"
10#include "detail/worker_pool.hpp"
11#include "task_tag.hpp"
12#include "task.hpp"
13#include "when_all.hpp"
14
15#ifndef DISPATCH_QUEUE_DEFAULT_BATCH_SIZE
16 #define DISPATCH_QUEUE_DEFAULT_BATCH_SIZE 64
17#endif
18
19namespace dispatch_queue {
20
22public:
28
34
46 template<typename Fn>
47 dispatch_queue(int thread_count, Fn&& worker_init) {
48 if (thread_count < 0) {
49 thread_count = std::thread::hardware_concurrency();
50 }
51 if (thread_count > 0) {
52 worker_pool = std::make_unique<detail::worker_pool>(task_queue, thread_count, std::move(worker_init));
53 }
54 }
55
58
63
71 template<typename F, typename... Args, typename Ret = detail::function_result<F, Args...>>
72 task<Ret> dispatch(F&& f, Args&&... args) {
73 return dispatch_internal(detail::task_type::background, NULL_TAG, std::forward<F>(f), std::forward<Args>(args)...);
74 }
75
84 template<typename F, typename... Args, typename Ret = detail::function_result<F, Args...>>
85 task<Ret> dispatch_main(F&& f, Args&&... args) {
86 return dispatch_internal(detail::task_type::main, NULL_TAG, std::forward<F>(f), std::forward<Args>(args)...);
87 }
88
99 template<typename F, typename... Args, typename Ret = detail::function_result<F, Args...>>
100 task<Ret> dispatch_tagged(task_tag tag, F&& f, Args&&... args) {
101 return dispatch_internal(detail::task_type::tagged, tag, std::forward<F>(f), std::forward<Args>(args)...);
102 }
103
110 template<typename F, typename It>
111 task<void> parallel_for(F&& f, const It& begin, const It& end, size_t batch_size = DISPATCH_QUEUE_DEFAULT_BATCH_SIZE) {
112 std::vector<task<void>> tasks;
113 detail::apply_batches([&](auto&& batch_begin, auto&& batch_end) {
114 tasks.emplace_back(dispatch([=] {
115 for (auto it = batch_begin; it != batch_end; ++it) {
116 f(*it);
117 }
118 }));
119 }, begin, end, batch_size);
120 return when_all(tasks);
121 }
122
129 template<typename F, typename R>
130 task<void> parallel_for(F&& f, R&& range, size_t batch_size = DISPATCH_QUEUE_DEFAULT_BATCH_SIZE) {
131 std::vector<task<void>> tasks;
132 detail::apply_batches([&](auto&& batch_begin, auto&& batch_end) {
133 tasks.emplace_back(dispatch([=] {
134 for (auto it = batch_begin; it != batch_end; ++it) {
135 f(*it);
136 }
137 }));
138 }, range, batch_size);
139 return when_all(tasks);
140 }
141
145 bool is_threaded() const;
146
151 int thread_count() const;
152
156 size_t size() const;
157
161 bool empty() const;
162
167 void clear();
168
173 void main_loop();
174
178 void wait();
179
185 template<class Rep, class Period>
186 bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) {
187 if (worker_pool) {
188 return worker_pool->wait_for(timeout_duration);
189 }
190 else {
191 return true;
192 }
193 }
194
200 template<class Clock, class Duration>
201 bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) {
202 if (worker_pool) {
203 return worker_pool->wait_until(timeout_time);
204 }
205 else {
206 return true;
207 }
208 }
209
215 void shutdown();
216
217#ifdef __cpp_lib_coroutine
218private:
219 struct dispatch_awaiter {
221
222 bool await_ready() const noexcept { return false; }
223 void await_suspend(std::coroutine_handle<> cont) const {
224 dispatch_queue.dispatch([cont]{
225 cont();
226 if (cont.done()) {
227 cont.destroy();
228 }
229 });
230 }
231 void await_resume() {}
232 };
233
234 struct dispatch_main_awaiter {
235 dispatch_queue& dispatch_queue;
236
237 bool await_ready() const noexcept { return false; }
238 void await_suspend(std::coroutine_handle<> cont) const {
239 dispatch_queue.dispatch_main([cont]{
240 cont();
241 if (cont.done()) {
242 cont.destroy();
243 }
244 });
245 }
246 void await_resume() {}
247 };
248
249 struct dispatch_tagged_awaiter {
250 dispatch_queue& dispatch_queue;
251 task_tag tag;
252
253 bool await_ready() const noexcept { return false; }
254 void await_suspend(std::coroutine_handle<> cont) const {
255 dispatch_queue.dispatch_tagged(tag, [cont]{
256 cont();
257 if (cont.done()) {
258 cont.destroy();
259 }
260 });
261 }
262 void await_resume() {}
263 };
264public:
275 dispatch_awaiter dispatch() {
276 return dispatch_awaiter(*this);
277 }
278
289 dispatch_main_awaiter dispatch_main() {
290 return dispatch_main_awaiter(*this);
291 }
292
303 dispatch_tagged_awaiter dispatch_tagged(task_tag tag) {
304 return dispatch_tagged_awaiter(*this, tag);
305 }
306#endif
307
308private:
309 std::unique_ptr<detail::worker_pool> worker_pool;
310 detail::pending_task_queue task_queue;
311
312 template<typename F, typename... Args, typename Ret = detail::function_result<F, Args...>>
313 task<Ret> dispatch_internal(detail::task_type type, task_tag tag, F&& f, Args&&... args) {
314 auto work = std::bind(std::move(f), std::forward<Args>(args)...);
315 if (worker_pool) {
316 auto future = detail::task_future<Ret>::create_pending();
317 worker_pool->enqueue_task(type, { future->wrap(work) }, tag);
318 return task<Ret>(future);
319 }
320 else if (type == detail::task_type::main) {
321 auto future = detail::task_future<Ret>::create_pending();
322 task_queue.push(type, { future->wrap(work) });
323 return task<Ret>(future);
324 }
325 else {
326 auto future = detail::task_future<Ret>::create(work);
327 return task<Ret>(future);
328 }
329 }
330};
331
332} // end namespace dispatch_queue
dispatch_tagged_awaiter dispatch_tagged(task_tag tag)
Definition dispatch_queue.hpp:303
task< Ret > dispatch_main(F &&f, Args &&... args)
Definition dispatch_queue.hpp:85
dispatch_main_awaiter dispatch_main()
Definition dispatch_queue.hpp:289
bool wait_for(const std::chrono::duration< Rep, Period > &timeout_duration)
Definition dispatch_queue.hpp:186
dispatch_queue & operator=(const dispatch_queue &)=delete
dispatch_awaiter dispatch()
Definition dispatch_queue.hpp:275
task< void > parallel_for(F &&f, const It &begin, const It &end, size_t batch_size=DISPATCH_QUEUE_DEFAULT_BATCH_SIZE)
Definition dispatch_queue.hpp:111
dispatch_queue(const dispatch_queue &)=delete
int thread_count() const
Definition dispatch_queue.cpp:22
task< void > parallel_for(F &&f, R &&range, size_t batch_size=DISPATCH_QUEUE_DEFAULT_BATCH_SIZE)
Definition dispatch_queue.hpp:130
task< Ret > dispatch(F &&f, Args &&... args)
Definition dispatch_queue.hpp:72
task< Ret > dispatch_tagged(task_tag tag, F &&f, Args &&... args)
Definition dispatch_queue.hpp:100
bool wait_until(const std::chrono::time_point< Clock, Duration > &timeout_time)
Definition dispatch_queue.hpp:201
dispatch_queue(int thread_count, Fn &&worker_init)
Definition dispatch_queue.hpp:47
Definition task.hpp:32
#define DISPATCH_QUEUE_DEFAULT_BATCH_SIZE
Definition dispatch_queue.hpp:16
Definition dispatch_queue.hpp:19
int task_tag
Definition task_tag.hpp:12
task< void > when_all(std::initializer_list< task< T > > tasks)
Definition when_all.hpp:66