HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
scoped_task.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "utility/module.hpp"
8#include "notifier.hpp"
9#include "counters.hpp"
10#include "awaitable_timer.hpp"
11#include <coroutine>
12#include <type_traits>
13#include <memory>
14#include <exception>
15
16namespace hi::inline v1 {
17
27template<typename T = void>
29public:
30 using value_type = T;
31 using notifier_type = notifier<void(value_type)>;
32 using callback_token = notifier_type::callback_token;
33 using callback_proto = notifier_type::callback_proto;
34
35 struct promise_type {
37 std::optional<value_type> value = {};
38 std::exception_ptr exception = nullptr;
39
40 void return_value(std::convertible_to<value_type> auto&& new_value) noexcept
41 {
42 value = hi_forward(new_value);
43 }
44
45 void unhandled_exception() noexcept
46 {
47 exception = std::current_exception();
48 }
49
50 std::suspend_always final_suspend() noexcept
51 {
52 if (value) {
53 // Trigger the notifier with the co_return value.
54 notifier(*value);
55
56 } else {
57 // Notify also in case of exception.
58 hi_assert_not_null(exception);
59 if constexpr (std::is_default_constructible_v<value_type>) {
60 notifier(value_type{});
61 }
62 }
63
64 return {};
65 }
66
67 scoped_task get_return_object() noexcept
68 {
69 return scoped_task{handle_type::from_promise(*this)};
70 }
71
74 std::suspend_never initial_suspend() noexcept
75 {
76 return {};
77 }
78 };
79
80 using handle_type = std::coroutine_handle<promise_type>;
81
82 scoped_task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
83
84 ~scoped_task()
85 {
86 if (_coroutine) {
87 _coroutine.destroy();
88 }
89 }
90
91 scoped_task() = default;
92
93 // scoped_task can not be copied because it tracks if the co-routine must be destroyed by the
94 // shared_ptr to the value shared between scoped_task and the promise.
95 scoped_task(scoped_task const&) = delete;
96 scoped_task& operator=(scoped_task const&) = delete;
97
98 scoped_task(scoped_task&& other) noexcept
99 {
100 _coroutine = std::exchange(other._coroutine, {});
101 }
102
103 scoped_task& operator=(scoped_task&& other) noexcept
104 {
105 _coroutine = std::exchange(other._coroutine, {});
106 return *this;
107 }
108
111 [[nodiscard]] bool done() const noexcept
112 {
113 hi_axiom(_coroutine);
114 return _coroutine.done();
115 }
116
119 explicit operator bool() const noexcept
120 {
121 return done();
122 }
123
129 [[nodiscard]] value_type const& value() const
130 {
131 hi_axiom(done());
132
133 hilet& promise = _coroutine.promise();
134 if (promise.value) {
135 return *promise.value;
136
137 } else {
138 hi_axiom(promise.exception);
139 std::rethrow_exception(promise.exception);
140 }
141 }
142
148 [[nodiscard]] value_type const& operator*() const
149 {
150 return value();
151 }
152
160 callback_token subscribe(forward_of<callback_proto> auto&& callback, callback_flags flags = callback_flags::synchronous) noexcept
161 {
162 return _coroutine.promise().notifier.subscribe(hi_forward(callback), flags);
163 }
164
165private:
166 // Optional value type
167 handle_type _coroutine;
168};
169
173template<>
174class scoped_task<void> {
175public:
176 using value_type = void;
178 using callback_token = notifier_type::callback_token;
179 using callback_proto = notifier_type::callback_proto;
180
181 struct promise_type {
183 std::exception_ptr exception = nullptr;
184
185 void return_void() noexcept {}
186
187 void unhandled_exception() noexcept
188 {
189 exception = std::current_exception();
190 }
191
192 std::suspend_always final_suspend() noexcept
193 {
194 notifier();
195 return {};
196 }
197
198 scoped_task get_return_object() noexcept
199 {
200 return scoped_task{handle_type::from_promise(*this)};
201 }
202
203 std::suspend_never initial_suspend() noexcept
204 {
205 return {};
206 }
207 };
208
209 using handle_type = std::coroutine_handle<promise_type>;
210
211 scoped_task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
212
214 {
215 if (_coroutine) {
216 _coroutine.destroy();
217 }
218 }
219
220 scoped_task() = default;
221 scoped_task(scoped_task const&) = delete;
222 scoped_task& operator=(scoped_task const&) = delete;
223
224 scoped_task(scoped_task&& other) noexcept
225 {
226 _coroutine = std::exchange(other._coroutine, {});
227 }
228
229 scoped_task& operator=(scoped_task&& other) noexcept
230 {
231 _coroutine = std::exchange(other._coroutine, {});
232 return *this;
233 }
234
238 [[nodiscard]] bool done() const noexcept
239 {
240 hi_axiom(_coroutine);
241 return _coroutine.done();
242 }
243
247 explicit operator bool() const noexcept
248 {
249 return done();
250 }
251
257 void value() const
258 {
259 hi_axiom(done());
260
261 hilet& promise = _coroutine.promise();
262 if (promise.exception) {
263 std::rethrow_exception(promise.exception);
264 }
265 }
266
270 callback_token subscribe(forward_of<callback_proto> auto&& callback, callback_flags flags = callback_flags::synchronous) noexcept
271 {
272 return _coroutine.promise().notifier.subscribe(hi_forward(callback), flags);
273 }
274
275private:
276 // Optional value type
277 handle_type _coroutine;
278};
279
280} // namespace hi::inline v1
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:13
callback_flags
Definition callback_flags.hpp:11
A scoped_task.
Definition scoped_task.hpp:28
value_type const & operator*() const
Get the return value returned from co_return.
Definition scoped_task.hpp:148
callback_token subscribe(forward_of< callback_proto > auto &&callback, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback for when the co-routine is completed.
Definition scoped_task.hpp:160
value_type const & value() const
Get the return value returned from co_return.
Definition scoped_task.hpp:129
bool done() const noexcept
Check if the co-routine has completed.
Definition scoped_task.hpp:111
Definition scoped_task.hpp:35
std::suspend_never initial_suspend() noexcept
Before we enter the coroutine, allow the caller to set the callback.
Definition scoped_task.hpp:74
callback_token subscribe(forward_of< callback_proto > auto &&callback, callback_flags flags=callback_flags::synchronous) noexcept
Definition scoped_task.hpp:270
void value() const
Get the return value returned from co_return.
Definition scoped_task.hpp:257
bool done() const noexcept
Definition scoped_task.hpp:238
True if T is a forwarded type of Forward.
Definition concepts.hpp:130
T current_exception(T... args)
T rethrow_exception(T... args)