ThreadSchedule 1.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
task.hpp
Go to the documentation of this file.
1#pragma once
2
13
14#if defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L
15
16#include <atomic>
17#include <coroutine>
18#include <exception>
19#include <optional>
20#include <type_traits>
21#include <utility>
22
23namespace threadschedule
24{
25
26template <typename T = void>
27class task;
28
29namespace detail
30{
31
43struct final_awaiter
44{
45 [[nodiscard]] auto await_ready() const noexcept -> bool
46 {
47 return false;
48 }
49
50 template <typename Promise>
51 auto await_suspend(std::coroutine_handle<Promise> h) const noexcept -> std::coroutine_handle<>
52 {
53 if (auto cont = h.promise().continuation_; cont)
54 return cont;
55 return std::noop_coroutine();
56 }
57
58 void await_resume() const noexcept
59 {
60 }
61};
62
82template <typename T>
83class task_promise_base
84{
85 public:
86 task_promise_base() = default;
87
88 auto initial_suspend() noexcept -> std::suspend_always
89 {
90 return {};
91 }
92
93 auto final_suspend() noexcept -> final_awaiter
94 {
95 return {};
96 }
97
98 void unhandled_exception() noexcept
99 {
100 exception_ = std::current_exception();
101 }
102
103 void rethrow_if_exception()
104 {
105 if (exception_)
106 std::rethrow_exception(exception_);
107 }
108
109 std::coroutine_handle<> continuation_{};
110
111 protected:
112 std::exception_ptr exception_{};
113};
114
115} // namespace detail
116
117// --- task<T> (non-void) ---
118
150template <typename T>
151class task
152{
153 public:
154 struct promise_type : detail::task_promise_base<T>
155 {
156 auto get_return_object() noexcept -> task
157 {
158 return task{std::coroutine_handle<promise_type>::from_promise(*this)};
159 }
160
161 void return_value(T value) noexcept(std::is_nothrow_move_constructible_v<T>)
162 {
163 result_.emplace(std::move(value));
164 }
165
166 auto result() -> T
167 {
168 this->rethrow_if_exception();
169 return std::move(*result_);
170 }
171
172 private:
173 std::optional<T> result_{};
174 };
175
176 task() noexcept = default;
177
178 task(task&& other) noexcept : handle_(std::exchange(other.handle_, nullptr))
179 {
180 }
181
182 auto operator=(task&& other) noexcept -> task&
183 {
184 if (this != &other)
185 {
186 destroy();
187 handle_ = std::exchange(other.handle_, nullptr);
188 }
189 return *this;
190 }
191
192 ~task()
193 {
194 destroy();
195 }
196
197 task(task const&) = delete;
198 auto operator=(task const&) -> task& = delete;
199
200 [[nodiscard]] auto handle() const noexcept -> std::coroutine_handle<promise_type>
201 {
202 return handle_;
203 }
204
205 struct awaiter
206 {
207 std::coroutine_handle<promise_type> handle_;
208
209 [[nodiscard]] auto await_ready() const noexcept -> bool
210 {
211 return false;
212 }
213
214 auto await_suspend(std::coroutine_handle<> continuation) noexcept -> std::coroutine_handle<>
215 {
216 handle_.promise().continuation_ = continuation;
217 return handle_;
218 }
219
220 auto await_resume() -> T
221 {
222 return handle_.promise().result();
223 }
224 };
225
226 auto operator co_await() const& noexcept -> awaiter
227 {
228 return awaiter{handle_};
229 }
230
231 auto operator co_await() const&& noexcept -> awaiter
232 {
233 return awaiter{handle_};
234 }
235
236 private:
237 explicit task(std::coroutine_handle<promise_type> h) noexcept : handle_(h)
238 {
239 }
240
241 void destroy()
242 {
243 if (handle_)
244 {
245 handle_.destroy();
246 handle_ = nullptr;
247 }
248 }
249
250 std::coroutine_handle<promise_type> handle_{};
251};
252
253// --- task<void> ---
254
282template <>
283class task<void>
284{
285 public:
286 struct promise_type : detail::task_promise_base<void>
287 {
288 auto get_return_object() noexcept -> task<void>
289 {
290 return task<void>{std::coroutine_handle<promise_type>::from_promise(*this)};
291 }
292
293 void return_void() noexcept
294 {
295 }
296
297 void result()
298 {
299 this->rethrow_if_exception();
300 }
301 };
302
303 task() noexcept = default;
304
305 task(task&& other) noexcept : handle_(std::exchange(other.handle_, nullptr))
306 {
307 }
308
309 auto operator=(task&& other) noexcept -> task<void>&
310 {
311 if (this != &other)
312 {
313 destroy();
314 handle_ = std::exchange(other.handle_, nullptr);
315 }
316 return *this;
317 }
318
319 ~task()
320 {
321 destroy();
322 }
323
324 task(task const&) = delete;
325 auto operator=(task const&) -> task<void>& = delete;
326
327 [[nodiscard]] auto handle() const noexcept -> std::coroutine_handle<promise_type>
328 {
329 return handle_;
330 }
331
332 struct awaiter
333 {
334 std::coroutine_handle<promise_type> handle_;
335
336 [[nodiscard]] auto await_ready() const noexcept -> bool
337 {
338 return false;
339 }
340
341 auto await_suspend(std::coroutine_handle<> continuation) noexcept -> std::coroutine_handle<>
342 {
343 handle_.promise().continuation_ = continuation;
344 return handle_;
345 }
346
347 void await_resume()
348 {
349 handle_.promise().result();
350 }
351 };
352
353 auto operator co_await() const& noexcept -> awaiter
354 {
355 return awaiter{handle_};
356 }
357
358 auto operator co_await() const&& noexcept -> awaiter
359 {
360 return awaiter{handle_};
361 }
362
363 private:
364 explicit task(std::coroutine_handle<promise_type> h) noexcept : handle_(h)
365 {
366 }
367
368 void destroy()
369 {
370 if (handle_)
371 {
372 handle_.destroy();
373 handle_ = nullptr;
374 }
375 }
376
377 std::coroutine_handle<promise_type> handle_{};
378};
379
380// --- sync_wait ---
381
382namespace detail
383{
384
405class sync_wait_task
406{
407 public:
408 struct promise_type
409 {
410 auto initial_suspend() noexcept -> std::suspend_always
411 {
412 return {};
413 }
414
415 auto final_suspend() noexcept
416 {
417 struct notifier
418 {
419 [[nodiscard]] auto await_ready() const noexcept -> bool
420 {
421 return false;
422 }
423
424 void await_suspend(std::coroutine_handle<promise_type> h) const noexcept
425 {
426 h.promise().finished_.store(true, std::memory_order_release);
427 h.promise().finished_.notify_one();
428 }
429
430 void await_resume() const noexcept
431 {
432 }
433 };
434 return notifier{};
435 }
436
437 auto get_return_object() -> sync_wait_task
438 {
439 return sync_wait_task{std::coroutine_handle<promise_type>::from_promise(*this)};
440 }
441
442 void return_void() noexcept
443 {
444 }
445
446 void unhandled_exception() noexcept
447 {
448 exception_ = std::current_exception();
449 }
450
451 std::atomic<bool> finished_{false};
452 std::exception_ptr exception_{};
453 };
454
455 explicit sync_wait_task(std::coroutine_handle<promise_type> h) : handle_(h)
456 {
457 }
458
459 sync_wait_task(sync_wait_task&& other) noexcept : handle_(std::exchange(other.handle_, nullptr))
460 {
461 }
462
463 ~sync_wait_task()
464 {
465 if (handle_)
466 handle_.destroy();
467 }
468
469 sync_wait_task(sync_wait_task const&) = delete;
470 auto operator=(sync_wait_task const&) -> sync_wait_task& = delete;
471 auto operator=(sync_wait_task&&) -> sync_wait_task& = delete;
472
473 void start()
474 {
475 handle_.resume();
476 }
477
478 void wait()
479 {
480 handle_.promise().finished_.wait(false, std::memory_order_acquire);
481 }
482
483 void rethrow()
484 {
485 if (handle_.promise().exception_)
486 std::rethrow_exception(handle_.promise().exception_);
487 }
488
489 private:
490 std::coroutine_handle<promise_type> handle_;
491};
492
493} // namespace detail
494
517template <typename T>
518auto sync_wait(task<T> t) -> T
519{
520 std::optional<T> result;
521 std::exception_ptr ex;
522
523 auto wrapper = [&result, &ex](task<T> inner) -> detail::sync_wait_task {
524 try
525 {
526 result.emplace(co_await inner);
527 }
528 catch (...)
529 {
530 ex = std::current_exception();
531 }
532 };
533
534 auto sw = wrapper(std::move(t));
535 sw.start();
536 sw.wait();
537 sw.rethrow();
538
539 if (ex)
540 std::rethrow_exception(ex);
541
542 return std::move(*result);
543}
544
558inline void sync_wait(task<void> t)
559{
560 std::exception_ptr ex;
561
562 auto wrapper = [&ex](task<void> inner) -> detail::sync_wait_task {
563 try
564 {
565 co_await inner;
566 }
567 catch (...)
568 {
569 ex = std::current_exception();
570 }
571 };
572
573 auto sw = wrapper(std::move(t));
574 sw.start();
575 sw.wait();
576 sw.rethrow();
577
578 if (ex)
579 std::rethrow_exception(ex);
580}
581
582} // namespace threadschedule
583
584#endif // __cpp_impl_coroutine