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/utility.hpp"
8#include "../concurrency/concurrency.hpp"
9#include "../macros.hpp"
10#include <coroutine>
11#include <type_traits>
12#include <memory>
13#include <exception>
14
15
16
17namespace hi::inline v1 {
18
28template<typename T = void>
30public:
31 using value_type = T;
32 using notifier_type = notifier<void(value_type)>;
33 using callback_token = notifier_type::callback_token;
34 using callback_proto = notifier_type::callback_proto;
35
36 struct promise_type {
38 std::optional<value_type> value = {};
39 std::exception_ptr exception = nullptr;
40
41 void return_value(std::convertible_to<value_type> auto&& new_value) noexcept
42 {
43 value = hi_forward(new_value);
44 }
45
46 void unhandled_exception() noexcept
47 {
48 exception = std::current_exception();
49 }
50
51 std::suspend_always final_suspend() noexcept
52 {
53 if (value) {
54 // Trigger the notifier with the co_return value.
55 notifier(*value);
56
57 } else {
58 // Notify also in case of exception.
59 hi_assert_not_null(exception);
60 if constexpr (std::is_default_constructible_v<value_type>) {
61 notifier(value_type{});
62 }
63 }
64
65 return {};
66 }
67
68 scoped_task get_return_object() noexcept
69 {
70 return scoped_task{handle_type::from_promise(*this)};
71 }
72
75 std::suspend_never initial_suspend() noexcept
76 {
77 return {};
78 }
79 };
80
81 using handle_type = std::coroutine_handle<promise_type>;
82
83 scoped_task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
84
85 ~scoped_task()
86 {
87 if (_coroutine) {
88 _coroutine.destroy();
89 }
90 }
91
92 scoped_task() = default;
93
94 // scoped_task can not be copied because it tracks if the co-routine must be destroyed by the
95 // shared_ptr to the value shared between scoped_task and the promise.
96 scoped_task(scoped_task const&) = delete;
97 scoped_task& operator=(scoped_task const&) = delete;
98
99 scoped_task(scoped_task&& other) noexcept
100 {
101 _coroutine = std::exchange(other._coroutine, {});
102 }
103
104 scoped_task& operator=(scoped_task&& other) noexcept
105 {
106 _coroutine = std::exchange(other._coroutine, {});
107 return *this;
108 }
109
112 [[nodiscard]] bool done() const noexcept
113 {
114 hi_axiom(_coroutine);
115 return _coroutine.done();
116 }
117
120 explicit operator bool() const noexcept
121 {
122 return done();
123 }
124
130 [[nodiscard]] value_type const& value() const
131 {
132 hi_axiom(done());
133
134 hilet& promise = _coroutine.promise();
135 if (promise.value) {
136 return *promise.value;
137
138 } else {
139 hi_axiom(promise.exception);
140 std::rethrow_exception(promise.exception);
141 }
142 }
143
149 [[nodiscard]] value_type const& operator*() const
150 {
151 return value();
152 }
153
161 callback_token subscribe(forward_of<callback_proto> auto&& callback, callback_flags flags = callback_flags::synchronous) noexcept
162 {
163 return _coroutine.promise().notifier.subscribe(hi_forward(callback), flags);
164 }
165
166private:
167 // Optional value type
168 handle_type _coroutine;
169};
170
174template<>
176public:
177 using value_type = void;
179 using callback_token = notifier_type::callback_token;
180 using callback_proto = notifier_type::callback_proto;
181
182 struct promise_type {
184 std::exception_ptr exception = nullptr;
185
186 void return_void() noexcept {}
187
188 void unhandled_exception() noexcept
189 {
190 exception = std::current_exception();
191 }
192
193 std::suspend_always final_suspend() noexcept
194 {
195 notifier();
196 return {};
197 }
198
199 scoped_task get_return_object() noexcept
200 {
201 return scoped_task{handle_type::from_promise(*this)};
202 }
203
204 std::suspend_never initial_suspend() noexcept
205 {
206 return {};
207 }
208 };
209
210 using handle_type = std::coroutine_handle<promise_type>;
211
212 scoped_task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
213
215 {
216 if (_coroutine) {
217 _coroutine.destroy();
218 }
219 }
220
221 scoped_task() = default;
222 scoped_task(scoped_task const&) = delete;
223 scoped_task& operator=(scoped_task const&) = delete;
224
225 scoped_task(scoped_task&& other) noexcept
226 {
227 _coroutine = std::exchange(other._coroutine, {});
228 }
229
230 scoped_task& operator=(scoped_task&& other) noexcept
231 {
232 _coroutine = std::exchange(other._coroutine, {});
233 return *this;
234 }
235
239 [[nodiscard]] bool done() const noexcept
240 {
241 hi_axiom(_coroutine);
242 return _coroutine.done();
243 }
244
248 explicit operator bool() const noexcept
249 {
250 return done();
251 }
252
258 void value() const
259 {
260 hi_axiom(done());
261
262 hilet& promise = _coroutine.promise();
263 if (promise.exception) {
264 std::rethrow_exception(promise.exception);
265 }
266 }
267
271 callback_token subscribe(forward_of<callback_proto> auto&& callback, callback_flags flags = callback_flags::synchronous) noexcept
272 {
273 return _coroutine.promise().notifier.subscribe(hi_forward(callback), flags);
274 }
275
276private:
277 // Optional value type
278 handle_type _coroutine;
279};
280
281} // namespace hi::inline v1
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:16
callback_flags
Definition callback_flags.hpp:14
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
A scoped_task.
Definition scoped_task.hpp:29
value_type const & operator*() const
Get the return value returned from co_return.
Definition scoped_task.hpp:149
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:161
value_type const & value() const
Get the return value returned from co_return.
Definition scoped_task.hpp:130
bool done() const noexcept
Check if the co-routine has completed.
Definition scoped_task.hpp:112
Definition scoped_task.hpp:36
std::suspend_never initial_suspend() noexcept
Before we enter the coroutine, allow the caller to set the callback.
Definition scoped_task.hpp:75
callback_token subscribe(forward_of< callback_proto > auto &&callback, callback_flags flags=callback_flags::synchronous) noexcept
Definition scoped_task.hpp:271
void value() const
Get the return value returned from co_return.
Definition scoped_task.hpp:258
bool done() const noexcept
Definition scoped_task.hpp:239
T current_exception(T... args)
T rethrow_exception(T... args)