Dispatch Queue
Dispatch Queue / Thread Pool implementation for C++11 with built-in C++20 coroutine support
 
Loading...
Searching...
No Matches
task.hpp
Go to the documentation of this file.
1#pragma once
2
3#ifdef __cpp_lib_coroutine
4#include <coroutine>
5#endif
6#include <exception>
7#include <type_traits>
8
9#include "detail/function_result.hpp"
10#include "detail/is_instance_of.hpp"
11#include "detail/task_future.hpp"
12#include "task_error.hpp"
13#include "task_state.hpp"
14
15namespace dispatch_queue {
16
17#ifdef __cpp_exceptions
18 #define DISPATCH_QUEUE_THROW_OR(err_msg, body) throw task_error(err_msg)
19#else
20 #define DISPATCH_QUEUE_THROW_OR(err_msg, body) body
21#endif
22
31template<typename T>
32class task {
33public:
34 using value_type = T;
35
39 task() = default;
40 task(std::shared_ptr<detail::task_future<T>> future)
41 : future(future)
42 {
43 }
44
49 return detail::task_future<T>::create_pending();
50 }
51
55 template<typename U = T, typename = typename std::enable_if<std::is_void<U>::value>::type>
56 static task create_ready() {
57 return detail::task_future<T>::create_ready();
58 }
59
63 template<typename U = T, typename = typename std::enable_if<not std::is_void<U>::value>::type>
64 static task create_ready(U&& value) {
65 return detail::task_future<T>::create_ready(std::move(value));
66 }
67
71 static task create_failed(std::exception_ptr exception) {
72 return detail::task_future<T>::create_failed(exception);
73 }
74
78 bool valid() const {
79 return (bool)future;
80 }
81
82#ifdef __cpp_concepts
91 template<typename F>
92 requires (detail::is_instance_of<T, task>::value)
93 auto then(F&& f) const {
94 if (future) {
95 task value_this = *this;
96 auto nested_future = detail::task_future<detail::function_result<F, T>>::create_pending();
97 future->then([=] {
98 value_this.get().then([=](const auto& t) {
99 nested_future->do_work(f, t);
100 });
101 });
102 return to_task(nested_future);
103 }
104 else {
105 using future_t = detail::task_future<detail::function_result<F, T>>;
106 DISPATCH_QUEUE_THROW_OR("task is invalid", {
107 auto work = std::bind(f, T{});
108 auto future = future_t::create(std::move(work));
109 return to_task(future);
110 });
111 }
112 }
113#endif
114
123 template<typename F>
124 auto then(F&& f) const {
125 if (future) {
126 task value_this = *this;
127 return to_task(future->then([=] {
128 return f(value_this);
129 }));
130 }
131 else {
132 using future_t = detail::task_future<detail::function_result<F, task>>;
133 DISPATCH_QUEUE_THROW_OR("task is invalid", {
134 auto work = std::bind(f, *this);
135 auto future = future_t::create(std::move(work));
136 return to_task(future);
137 });
138 }
139 }
140
147 T get() const {
148 if (future) {
149 return future->get();
150 }
151 else {
152 DISPATCH_QUEUE_THROW_OR("task is invalid", return T{});
153 }
154 }
155
161 template<typename U = T, typename = typename std::enable_if<std::is_void<U>::value>::type>
162 void set_value() {
163 if (future) {
164 if (!future->set_value()) {
165 DISPATCH_QUEUE_THROW_OR("task is not pending", return);
166 }
167 }
168 else {
169 DISPATCH_QUEUE_THROW_OR("task is invalid", return);
170 }
171 }
172
178 template<typename U = T, typename = typename std::enable_if<not std::is_void<U>::value>::type>
179 void set_value(U&& value) {
180 if (future) {
181 if (!future->set_value(std::move(value))) {
182 DISPATCH_QUEUE_THROW_OR("task is not pending", return);
183 }
184 }
185 else {
186 DISPATCH_QUEUE_THROW_OR("task is invalid", return);
187 }
188 }
189
194 if (future) {
195 return future->get_state();
196 }
197 else {
198 return task_state::invalid;
199 }
200 }
201
205 std::exception_ptr get_exception() const {
206 if (future) {
207 return future->get_exception();
208 }
209 else {
210 return std::make_exception_ptr(task_error("task is invalid"));
211 }
212 }
213
219 void set_exception(std::exception_ptr exception) {
220 if (future) {
221 if (!future->set_exception(exception)) {
222 DISPATCH_QUEUE_THROW_OR("task is not pending", return);
223 }
224 }
225 else {
226 DISPATCH_QUEUE_THROW_OR("task is invalid", return);
227 }
228 }
229
238 void wait() const {
239 if (future) {
240 future->wait();
241 }
242 else {
243 DISPATCH_QUEUE_THROW_OR("task is invalid", return);
244 }
245 }
246
257 template<class Rep, class Period>
258 bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
259 if (future) {
260 return future->wait_for(timeout_duration);
261 }
262 else {
263 DISPATCH_QUEUE_THROW_OR("task is invalid", return false);
264 }
265 }
266
277 template<class Clock, class Duration>
278 bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {
279 if (future) {
280 return future->wait_until(timeout_time);
281 }
282 else {
283 DISPATCH_QUEUE_THROW_OR("task is invalid", return false);
284 }
285 }
286
290 operator task<void>() const {
291 switch (get_state()) {
292 case task_state::pending: {
293 auto void_future = detail::task_future<void>::create_pending();
294 then([=](const task& t) {
295 if (auto exception = t.get_exception()) {
296 void_future->set_exception(exception);
297 }
298 else {
299 void_future->set_value();
300 }
301 });
302 return to_task(void_future);
303 }
304
307
310
311 default:
312 return {};
313 }
314 }
315
319 template<typename U, typename = typename std::enable_if<std::is_convertible<T, U>::value>::type>
320 explicit operator task<U>() const {
321 switch (get_state()) {
322 case task_state::pending: {
323 auto u_future = detail::task_future<U>::create_pending();
324 then([=](const task& t) {
325 if (auto exception = t.get_exception()) {
326 u_future->set_exception(exception);
327 }
328 else {
329 U u_value = (U) t.get();
330 u_future->set_value(std::move(u_value));
331 }
332 });
333 return to_task(u_future);
334 }
335
336 case task_state::ready: {
337 U u_value = (U) get();
338 return task<U>::create_ready(std::move(u_value));
339 }
340
343
344 default:
345 return {};
346 }
347 }
348
349#ifdef __cpp_lib_coroutine
350private:
351 class task_awaiter {
352 public:
353 task_awaiter(const task<T>& t) : t(t) {}
354 task_awaiter(task<T>&& t) : t(std::move(t)) {}
355
356 bool await_ready() const noexcept {
357 return t.get_state() != task_state::pending;
358 }
359
360 void await_suspend(std::coroutine_handle<> cont) const {
361 t.then([cont](auto&&) {
362 cont();
363 if (cont.done()) {
364 cont.destroy();
365 }
366 });
367 }
368
369 T await_resume() {
370 return t.get();
371 }
372
373 private:
374 task<T> t;
375 };
376
377public:
389 task_awaiter operator co_await() const {
390 return task_awaiter(*this);
391 }
392#endif
393
394private:
395 std::shared_ptr<detail::task_future<T>> future;
396
398 template<typename U>
399 static task<U> to_task(std::shared_ptr<detail::task_future<U>> future) {
400 return task<U>(future);
401 }
402};
403
404} // end namespace dispatch_queue
Definition task_error.hpp:10
Definition task.hpp:32
bool wait_for(const std::chrono::duration< Rep, Period > &timeout_duration) const
Definition task.hpp:258
T get() const
Definition task.hpp:147
task_state get_state() const
Definition task.hpp:193
void set_value()
Definition task.hpp:162
static task create_failed(std::exception_ptr exception)
Definition task.hpp:71
static task create_ready()
Definition task.hpp:56
bool valid() const
Definition task.hpp:78
static task create_pending()
Definition task.hpp:48
void set_value(U &&value)
Definition task.hpp:179
task(std::shared_ptr< detail::task_future< T > > future)
Definition task.hpp:40
void wait() const
Definition task.hpp:238
std::exception_ptr get_exception() const
Definition task.hpp:205
void set_exception(std::exception_ptr exception)
Definition task.hpp:219
bool wait_until(const std::chrono::time_point< Clock, Duration > &timeout_time) const
Definition task.hpp:278
auto then(F &&f) const
Definition task.hpp:124
T value_type
Definition task.hpp:34
static task create_ready(U &&value)
Definition task.hpp:64
Definition dispatch_queue.hpp:19
task_state
Definition task_state.hpp:9
@ failed
Task failed with an exception.
Definition task_state.hpp:17
@ pending
Task is either queued for execution or still running.
Definition task_state.hpp:13
@ ready
Task finished successfully and the result value is readily available.
Definition task_state.hpp:15
@ invalid
Task was created without a future and is invalid.
Definition task_state.hpp:11
#define DISPATCH_QUEUE_THROW_OR(err_msg, body)
Definition task.hpp:20