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 "required.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
33 struct promise_type {
35 std::optional<value_type> value = {};
36 std::exception_ptr exception = nullptr;
37
38 void return_value(std::convertible_to<value_type> auto&& new_value) noexcept
39 {
40 value = hi_forward(new_value);
41 }
42
43 void unhandled_exception() noexcept
44 {
45 exception = std::current_exception();
46 }
47
48 std::suspend_always final_suspend() noexcept
49 {
50 if (value) {
51 // Trigger the notifier with the co_return value.
52 notifier(*value);
53
54 } else {
55 // Notify also in case of exception.
56 hi_axiom(exception);
57 if constexpr (std::is_default_constructible_v<value_type>) {
58 notifier(value_type{});
59 }
60 }
61
62 return {};
63 }
64
65 scoped_task get_return_object() noexcept
66 {
67 return scoped_task{handle_type::from_promise(*this)};
68 }
69
72 std::suspend_never initial_suspend() noexcept
73 {
74 return {};
75 }
76 };
77
78 using handle_type = std::coroutine_handle<promise_type>;
79
80 scoped_task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
81
82 ~scoped_task()
83 {
84 if (_coroutine) {
85 _coroutine.destroy();
86 }
87 }
88
89 scoped_task() = default;
90
91 // scoped_task can not be copied because it tracks if the co-routine must be destroyed by the
92 // shared_ptr to the value shared between scoped_task and the promise.
93 scoped_task(scoped_task const&) = delete;
94 scoped_task& operator=(scoped_task const&) = delete;
95
96 scoped_task(scoped_task&& other) noexcept
97 {
98 _coroutine = std::exchange(other._coroutine, {});
99 }
100
101 scoped_task& operator=(scoped_task&& other) noexcept
102 {
103 _coroutine = std::exchange(other._coroutine, {});
104 return *this;
105 }
106
109 [[nodiscard]] bool done() const noexcept
110 {
111 hi_axiom(_coroutine);
112 return _coroutine.done();
113 }
114
117 explicit operator bool() const noexcept
118 {
119 return done();
120 }
121
127 [[nodiscard]] value_type const& value() const
128 {
129 hi_axiom(done());
130
131 hilet& promise = _coroutine.promise();
132 if (promise.value) {
133 return *promise.value;
134
135 } else {
136 hi_axiom(promise.exception);
137 std::rethrow_exception(promise.exception);
138 }
139 }
140
146 [[nodiscard]] value_type const& operator*() const
147 {
148 return value();
149 }
150
156 notifier_type::token_type subscribe(callback_flags flags, std::invocable<value_type> auto&& callback) noexcept
157 {
158 return _coroutine.promise().notifier.subscribe(flags, hi_forward(callback));
159 }
160
166 notifier_type::token_type subscribe(std::invocable<value_type> auto&& callback) noexcept
167 {
168 return subscribe(callback_flags::synchronous, hi_forward(callback));
169 }
170
171private:
172 // Optional value type
173 handle_type _coroutine;
174};
175
179template<>
180class scoped_task<void> {
181public:
182 using value_type = void;
184
185 struct promise_type {
187 std::exception_ptr exception = nullptr;
188
189 void return_void() noexcept {}
190
191 void unhandled_exception() noexcept
192 {
193 exception = std::current_exception();
194 }
195
196 std::suspend_always final_suspend() noexcept
197 {
198 notifier();
199 return {};
200 }
201
202 scoped_task get_return_object() noexcept
203 {
204 return scoped_task{handle_type::from_promise(*this)};
205 }
206
207 std::suspend_never initial_suspend() noexcept
208 {
209 return {};
210 }
211 };
212
213 using handle_type = std::coroutine_handle<promise_type>;
214
215 scoped_task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
216
218 {
219 if (_coroutine) {
220 _coroutine.destroy();
221 }
222 }
223
224 scoped_task() = default;
225 scoped_task(scoped_task const&) = delete;
226 scoped_task& operator=(scoped_task const&) = delete;
227
228 scoped_task(scoped_task&& other) noexcept
229 {
230 _coroutine = std::exchange(other._coroutine, {});
231 }
232
233 scoped_task& operator=(scoped_task&& other) noexcept
234 {
235 _coroutine = std::exchange(other._coroutine, {});
236 return *this;
237 }
238
242 [[nodiscard]] bool done() const noexcept
243 {
244 hi_axiom(_coroutine);
245 return _coroutine.done();
246 }
247
251 explicit operator bool() const noexcept
252 {
253 return done();
254 }
255
262 void value() const
263 {
264 hi_axiom(done());
265
266 hilet& promise = _coroutine.promise();
267 if (promise.exception) {
268 std::rethrow_exception(promise.exception);
269 }
270 }
271
275 notifier_type::token_type subscribe(callback_flags flags, std::invocable<> auto&& callback) noexcept
276 {
277 return _coroutine.promise().notifier.subscribe(flags, hi_forward(callback));
278 }
279
283 notifier_type::token_type subscribe(std::invocable<> auto&& callback) noexcept
284 {
285 return subscribe(callback_flags::synchronous, hi_forward(callback));
286 }
287
288private:
289 // Optional value type
290 handle_type _coroutine;
291};
292
293} // namespace hi::inline v1
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition required.hpp:29
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:146
notifier_type::token_type subscribe(callback_flags flags, std::invocable< value_type > auto &&callback) noexcept
Subscribe a callback for when the co-routine is completed.
Definition scoped_task.hpp:156
value_type const & value() const
Get the return value returned from co_return.
Definition scoped_task.hpp:127
notifier_type::token_type subscribe(std::invocable< value_type > auto &&callback) noexcept
Subscribe a callback for when the co-routine is completed.
Definition scoped_task.hpp:166
bool done() const noexcept
Check if the co-routine has completed.
Definition scoped_task.hpp:109
Definition scoped_task.hpp:33
std::suspend_never initial_suspend() noexcept
Before we enter the coroutine, allow the caller to set the callback.
Definition scoped_task.hpp:72
void value() const
Get the return value returned from co_return.
Definition scoped_task.hpp:262
bool done() const noexcept
Definition scoped_task.hpp:242
notifier_type::token_type subscribe(callback_flags flags, std::invocable<> auto &&callback) noexcept
Definition scoped_task.hpp:275
notifier_type::token_type subscribe(std::invocable<> auto &&callback) noexcept
Definition scoped_task.hpp:283
T current_exception(T... args)
T rethrow_exception(T... args)