HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
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 "notifier.hpp"
8#include "awaitable.hpp"
9#include "../utility/utility.hpp"
10#include "../concurrency/unfair_mutex.hpp" // XXX #616
11#include "../concurrency/thread.hpp" // XXX #616
12#include "../macros.hpp"
13#include <coroutine>
14#include <type_traits>
15#include <memory>
16#include <exception>
17#include <optional>
18
19hi_export_module(hikogui.dispatch : task);
20
21hi_export namespace hi::inline v1 {
22
30template<typename T = void, bool DestroyFrame = false>
31class task {
32public:
33 using value_type = T;
34 using notifier_type = notifier<void(value_type)>;
35 using callback_type = notifier_type::callback_type;
36
37 struct promise_type {
39 std::optional<value_type> value = {};
40 std::exception_ptr exception = nullptr;
41
42 template<std::convertible_to<value_type> NewValue>
43 void return_value(NewValue&& new_value) noexcept
44 {
45 value = std::forward<NewValue>(new_value);
46 }
47
48 void unhandled_exception() noexcept
49 {
50 exception = std::current_exception();
51 }
52
53 std::suspend_always final_suspend() noexcept
54 {
55 if (value) {
56 // Trigger the notifier with the co_return value.
57 notifier(*value);
58
59 } else {
60 // Notify also in case of exception.
61 hi_assert_not_null(exception);
62 if constexpr (std::is_default_constructible_v<value_type>) {
63 notifier(value_type{});
64 }
65 }
66
67 return {};
68 }
69
70 task get_return_object() noexcept
71 {
72 return task{handle_type::from_promise(*this)};
73 }
74
77 std::suspend_never initial_suspend() noexcept
78 {
79 return {};
80 }
81
82 template<convertible_to_awaitable RHS>
83 decltype(auto) await_transform(RHS&& rhs)
84 {
85 return awaitable_cast<std::remove_cvref_t<RHS>>{}(std::forward<RHS>(rhs));
86 }
87 };
88
89 using handle_type = std::coroutine_handle<promise_type>;
90
91 task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
92
93 ~task()
94 {
95 if (DestroyFrame and _coroutine) {
96 _coroutine.destroy();
97 }
98 }
99
100 task() = default;
101
102 // task can not be copied because it tracks if the co-routine must be destroyed by the
103 // shared_ptr to the value shared between task and the promise.
104 task(task const&) = delete;
105 task& operator=(task const&) = delete;
106
107 task(task&& other) noexcept
108 {
109 _coroutine = std::exchange(other._coroutine, {});
110 }
111
112 task& operator=(task&& other) noexcept
113 {
114 _coroutine = std::exchange(other._coroutine, {});
115 return *this;
116 }
117
120 [[nodiscard]] bool started() const noexcept
121 {
122 return _coroutine;
123 }
124
127 [[nodiscard]] bool running() const noexcept
128 {
129 return _coroutine and not _coroutine.done();
130 }
131
134 [[nodiscard]] bool done() const noexcept
135 {
136 return _coroutine and _coroutine.done();
137 }
138
144 [[nodiscard]] value_type const& value() const
145 {
146 hi_axiom(done());
147
148 auto const& promise = _coroutine.promise();
149 if (promise.value) {
150 return *promise.value;
151
152 } else {
153 hi_axiom(promise.exception);
154 std::rethrow_exception(promise.exception);
155 }
156 }
157
163 [[nodiscard]] value_type const& operator*() const
164 {
165 return value();
166 }
167
175 template<forward_of<void(value_type)> Func>
176 [[nodiscard]] callback<void(value_type)> subscribe(Func &&func, callback_flags flags = callback_flags::synchronous) noexcept
177 {
178 return _coroutine.promise().notifier.subscribe(std::forward<Func>(func), flags);
179 }
180
183 auto operator co_await() const noexcept
184 {
185 return _coroutine.promise().notifier.operator co_await();
186 }
187
188private:
189 // Optional value type
190 handle_type _coroutine;
191};
192
196template<bool DestroyFrame>
197class task<void, DestroyFrame> {
198public:
199 using value_type = void;
200 using notifier_type = notifier<void()>;
201 using callback_type = notifier_type::callback_type;
202
203 struct promise_type {
205 std::exception_ptr exception = nullptr;
206
207 void return_void() noexcept {}
208
209 void unhandled_exception() noexcept
210 {
211 exception = std::current_exception();
212 }
213
214 std::suspend_always final_suspend() noexcept
215 {
216 notifier();
217 return {};
218 }
219
220 task get_return_object() noexcept
221 {
222 return task{handle_type::from_promise(*this)};
223 }
224
225 std::suspend_never initial_suspend() noexcept
226 {
227 return {};
228 }
229
230 template<convertible_to_awaitable RHS>
231 decltype(auto) await_transform(RHS&& rhs)
232 {
233 return awaitable_cast<std::remove_cvref_t<RHS>>{}(std::forward<RHS>(rhs));
234 }
235 };
236
237 using handle_type = std::coroutine_handle<promise_type>;
238
239 task(handle_type coroutine) noexcept : _coroutine(coroutine) {}
240
241 ~task()
242 {
243 if (DestroyFrame and _coroutine) {
244 _coroutine.destroy();
245 }
246 }
247
248 task() = default;
249 task(task const&) = delete;
250 task& operator=(task const&) = delete;
251
252 task(task&& other) noexcept
253 {
254 _coroutine = std::exchange(other._coroutine, {});
255 }
256
257 task& operator=(task&& other) noexcept
258 {
259 _coroutine = std::exchange(other._coroutine, {});
260 return *this;
261 }
262
265 [[nodiscard]] bool started() const noexcept
266 {
267 return _coroutine;
268 }
269
272 [[nodiscard]] bool running() const noexcept
273 {
274 return _coroutine and not _coroutine.done();
275 }
276
279 [[nodiscard]] bool done() const noexcept
280 {
281 return _coroutine and _coroutine.done();
282 }
283
289 void value() const
290 {
291 hi_axiom(done());
292
293 auto const& promise = _coroutine.promise();
294 if (promise.exception) {
295 std::rethrow_exception(promise.exception);
296 }
297 }
298
302 template<forward_of<void()> Func>
303 [[nodiscard]] callback<void()> subscribe(Func &&func, callback_flags flags = callback_flags::synchronous) noexcept
304 {
305 return _coroutine.promise().notifier.subscribe(std::forward<Func>(func), flags);
306 }
307
310 auto operator co_await() const noexcept
311 {
312 return _coroutine.promise().notifier.operator co_await();
313 }
314
315private:
316 // Optional value type
317 handle_type _coroutine;
318};
319
320template<typename T = void>
321using scoped_task = task<T, true>;
322
325template<typename T>
327
328template<typename ResultType, bool DestroyFrame>
329struct is_task<hi::task<ResultType, DestroyFrame>> : std::true_type {};
330
333template<typename T>
335
336template<typename T>
338
339template<typename ResultType, bool DestroyFrame>
340struct task_value_type<hi::task<ResultType, DestroyFrame>> {
341 using type = ResultType;
342};
343
344template<typename T>
345using task_value_type_t = task_value_type<T>::type;
346
349template<typename Func, typename... ArgTypes>
351 constexpr static bool value = is_task_v<std::invoke_result_t<Func, ArgTypes...>>;
352};
353
356template<typename Func, typename... ArgTypes>
357constexpr bool is_invocable_task_v = is_invocable_task<Func, ArgTypes...>::value;
358
359template<typename Func, typename... ArgTypes>
361 using type = task_value_type_t<std::invoke_result_t<Func, ArgTypes...>>;
362};
363
364template<typename Func, typename... ArgTypes>
365using invoke_task_result_t = invoke_task_result<Func, ArgTypes...>::type;
366
367} // namespace hi::inline v1
Functions and types for accessing operating system threads.
Definition of the unfair_mutex.
@ other
The gui_event does not have associated data.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
constexpr bool is_invocable_task_v
type-trait to determining if the given invocable Func is a task.
Definition task.hpp:357
callback_flags
Definition callback_flags.hpp:15
constexpr bool is_task_v
type-trait to determine if the given type T is a task.
Definition task.hpp:334
Definition awaitable.hpp:41
A task.
Definition task.hpp:31
bool started() const noexcept
Check if the co-routine was started.
Definition task.hpp:120
bool done() const noexcept
Check if the co-routine has completed.
Definition task.hpp:134
callback< void(value_type)> subscribe(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback for when the co-routine is completed.
Definition task.hpp:176
value_type const & operator*() const
Get the return value returned from co_return.
Definition task.hpp:163
value_type const & value() const
Get the return value returned from co_return.
Definition task.hpp:144
bool running() const noexcept
Check if the co-routine is running.
Definition task.hpp:127
Definition task.hpp:37
std::suspend_never initial_suspend() noexcept
Before we enter the coroutine, allow the caller to set the callback.
Definition task.hpp:77
callback< void()> subscribe(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Definition task.hpp:303
bool started() const noexcept
Check if the co-routine was started.
Definition task.hpp:265
bool running() const noexcept
Check if the co-routine is running.
Definition task.hpp:272
bool done() const noexcept
Check if the co-routine has completed.
Definition task.hpp:279
void value() const
Get the return value returned from co_return.
Definition task.hpp:289
type-trait to determine if the given type T is a task.
Definition task.hpp:326
Definition task.hpp:337
type-trait to determining if the given invocable Func is a task.
Definition task.hpp:350
Definition task.hpp:360
T current_exception(T... args)
T rethrow_exception(T... args)