Fenix @develop
 
Loading...
Searching...
No Matches
promise.hpp
1#ifndef FENIX_TASKS_PROMISE_HPP
2#define FENIX_TASKS_PROMISE_HPP
3
4#include <coroutine>
5#include <exception>
6#include <cstdio>
7
8#include <mpi.h>
9
10#include "fenix/tasks/subtask.hpp"
11#include "fenix/tasks/awaiter.hpp"
12#include "fenix/mpixx/request.hpp"
13
14namespace fenix::tasks {
15
16namespace impl {
17template <typename T>
19 T val;
20 void return_value(const T& v) { val = v; }
21 T result() { return val; }
22};
23template <>
24struct ReturnHolder<void> {
25 void return_void() {};
26};
27}
28
29template <typename T, bool eager>
30class Task;
31
32template <typename T, bool eager = true>
33class Promise : public impl::ReturnHolder<T> {
34 public:
36 using TaskT = Task<T, eager>;
37 using HandleT = std::coroutine_handle<PromiseT>;
38
39 TaskT get_return_object() noexcept {
40 assert(!handle);
41 handle = HandleT::from_promise(*this);
42 return {this};
43 }
44
45 // Eagerly start tasks
46 auto initial_suspend() noexcept {
47 if constexpr (eager) return std::suspend_never{};
48 if constexpr (!eager) return std::suspend_always{};
49 }
50 // Don't destroy coroutine until object is destroyed
51 auto final_suspend() noexcept {
52 subtask.reset();
53 coro_done = true;
54 return std::suspend_always{};
55 }
56 // Rethrow exceptions immediately
57 void unhandled_exception() {
58 coro_done = true;
59 throw;
60 }
61
62 // Fixes some wonkiness in the standard and in the compiler implementations
63 // of coroutine cleanup w/ exceptions.
64 void register_owning_ptr(PromiseT** ptr) { owning_ptr = ptr; }
65 ~Promise() {
66 if (owning_ptr) *owning_ptr = nullptr;
67 }
68
69 void destroy() { handle.destroy(); }
70 bool done() { return coro_done; }
71
72 void resume() {
73 if (done()) return;
74 if (subtask) {
75 if (await_mode == AwaitMode::Blocking) subtask->wait();
76 else subtask->resume();
77 if (subtask->done()) subtask.reset();
78 }
79 if (!subtask) handle.resume();
80 }
81 void wait() {
82 await_mode = AwaitMode::Blocking;
83 while (!done()) resume();
84 }
85
86 template <Subtaskable U>
87 Awaiter<U> await_transform(U&& u) {
88 if constexpr (std::is_base_of_v<SubtaskBase, U>) {
89 subtask = std::make_shared<U>(std::forward<U>(u));
90 } else {
91 subtask = std::make_shared<Subtask<U>>(std::forward<U>(u));
92 }
93 return subtask.get();
94 }
95 Awaiter<mpixx::Request> await_transform(MPI_Request*& r) {
96 return await_transform(mpixx::Request(r));
97 }
98 Awaiter<mpixx::Request> await_transform(MPI_Request& r) {
99 return await_transform(mpixx::Request(&r));
100 }
101 auto await_transform(const std::suspend_always& s) {
102 subtask.reset();
103 return std::suspend_always{};
104 }
105
106 HandleT handle;
107 bool coro_done = false;
108 AwaitMode await_mode = AwaitMode::NonBlocking;
109 std::shared_ptr<SubtaskBase> subtask;
110 PromiseT** owning_ptr = nullptr;
111};
112
113} // namespace fenix::tasks
114
115#endif //FENIX_TASKS_PROMISE_HPP
Definition request.hpp:14
Definition awaiter.hpp:14
Definition promise.hpp:33
Definition task.hpp:15
Definition promise.hpp:18