KDDockWidgets API Documentation 2.0
Loading...
Searching...
No Matches
qcorotask.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2021 Daniel Vrátil <dvratil@kde.org>
2//
3// SPDX-License-Identifier: MIT
4
5// Only used by a KDDW Flutter build and only by the tests
6
7#pragma once
8
9#include "concepts_p.h"
10#include "coroutine.h"
11
12#include <atomic>
13#include <cassert>
14#include <memory>
15#include <optional>
16#include <type_traits>
17#include <variant>
18#include <vector>
19
20namespace QCoro {
21
22template<typename T = void>
23class Task;
24
27namespace detail {
28
29template<typename T>
30struct awaiter_type;
31
32template<typename T>
33using awaiter_type_t = typename awaiter_type<T>::type;
34
36class TaskFinalSuspend {
37public:
39
43 explicit TaskFinalSuspend(const std::vector<std::coroutine_handle<>> &awaitingCoroutines)
44 : mAwaitingCoroutines(awaitingCoroutines) {}
45
47
53 bool await_ready() const noexcept {
54 return false;
55 }
56
58
67 template<typename Promise>
68 void await_suspend(std::coroutine_handle<Promise> finishedCoroutine) noexcept {
69 auto &promise = finishedCoroutine.promise();
70
71 for (auto &awaiter : mAwaitingCoroutines) {
72 awaiter.resume();
73 }
74 mAwaitingCoroutines.clear();
75
76 // The handle will be destroyed here only if the associated Task has already been destroyed
77 if (promise.setDestroyHandle()) {
78 finishedCoroutine.destroy();
79 }
80 }
81
83
87 constexpr void await_resume() const noexcept {}
88
89private:
90 std::vector<std::coroutine_handle<>> mAwaitingCoroutines;
91};
92
94
132class TaskPromiseBase {
133public:
135
141 std::suspend_never initial_suspend() const noexcept {
142 return {};
143 }
144
146
149 auto final_suspend() const noexcept {
150 return TaskFinalSuspend{mAwaitingCoroutines};
151 }
152
154
168 template<typename T, typename Awaiter = QCoro::detail::awaiter_type_t<std::remove_cvref_t<T>>>
169 auto await_transform(T &&value) {
170 return Awaiter{value};
171 }
172
174
194 template<typename T>
195 auto await_transform(QCoro::Task<T> &&task) {
196 return std::forward<QCoro::Task<T>>(task);
197 }
198
200 template<typename T>
201 auto &await_transform(QCoro::Task<T> &task) {
202 return task;
203 }
204
206 template<Awaitable T>
207 auto &&await_transform(T &&awaitable) {
208 return std::forward<T>(awaitable);
209 }
210
212 template<Awaitable T>
213 auto &await_transform(T &awaitable) {
214 return awaitable;
215 }
216
218
227 void addAwaitingCoroutine(std::coroutine_handle<> awaitingCoroutine) {
228 mAwaitingCoroutines.push_back(awaitingCoroutine);
229 }
230
231 bool hasAwaitingCoroutine() const {
232 return !mAwaitingCoroutines.empty();
233 }
234
235 bool setDestroyHandle() noexcept {
236 return mDestroyHandle.exchange(true, std::memory_order_acq_rel);
237 }
238
239private:
240 friend class TaskFinalSuspend;
241
243 std::vector<std::coroutine_handle<>> mAwaitingCoroutines;
244
246 std::atomic<bool> mDestroyHandle{false};
247};
248
250
253template<typename T>
254class TaskPromise final : public TaskPromiseBase {
255public:
256 explicit TaskPromise() = default;
257 ~TaskPromise() = default;
258
260 Task<T> get_return_object() noexcept;
261
263
269 void unhandled_exception() {
270 mValue = std::current_exception();
271 }
272
274
278 void return_value(T &&value) noexcept {
279 mValue.template emplace<T>(std::forward<T>(value));
280 }
281
283 void return_value(const T &value) noexcept {
284 mValue = value;
285 }
286
287 template<typename U>
288 requires QCoro::concepts::constructible_from<T, U>
289 void return_value(U &&value) noexcept {
290 mValue = std::move(value);
291 }
292
293 template<typename U>
294 requires QCoro::concepts::constructible_from<T, U>
295 void return_value(const U &value) noexcept {
296 mValue = value;
297 }
298
300
304 T &result() & {
305 if (std::holds_alternative<std::exception_ptr>(mValue)) {
306 assert(std::get<std::exception_ptr>(mValue) != nullptr);
307 std::rethrow_exception(std::get<std::exception_ptr>(mValue));
308 }
309
310 return std::get<T>(mValue);
311 }
312
314 T &&result() && {
315 if (std::holds_alternative<std::exception_ptr>(mValue)) {
316 assert(std::get<std::exception_ptr>(mValue) != nullptr);
317 std::rethrow_exception(std::get<std::exception_ptr>(mValue));
318 }
319
320 return std::move(std::get<T>(mValue));
321 }
322
323private:
325 std::variant<std::monostate, T, std::exception_ptr> mValue;
326};
327
329template<>
330class TaskPromise<void> final : public TaskPromiseBase {
331public:
332 // Constructor.
333 explicit TaskPromise() = default;
334
336 ~TaskPromise() = default;
337
339 Task<void> get_return_object() noexcept;
340
342 void unhandled_exception() {
343 mException = std::current_exception();
344 }
345
347 void return_void() noexcept {};
348
350
355 void result() {
356 if (mException) {
357 std::rethrow_exception(mException);
358 }
359 }
360
361private:
363 std::exception_ptr mException;
364};
365
367template<typename Promise>
368class TaskAwaiterBase {
369public:
371 bool await_ready() const noexcept {
372 return !mAwaitedCoroutine || mAwaitedCoroutine.done();
373 }
374
376
407 void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
408 mAwaitedCoroutine.promise().addAwaitingCoroutine(awaitingCoroutine);
409 }
410
411protected:
413
416 TaskAwaiterBase(std::coroutine_handle<Promise> awaitedCoroutine)
417 : mAwaitedCoroutine(awaitedCoroutine) {}
418
420 std::coroutine_handle<Promise> mAwaitedCoroutine = {};
421};
422
423template<typename T>
424struct isTask : std::false_type {
425 using return_type = T;
426};
427
428template<typename T>
429struct isTask<QCoro::Task<T>> : std::true_type {
430 using return_type = typename QCoro::Task<T>::value_type;
431};
432
433template<typename T>
434constexpr bool isTask_v = isTask<T>::value;
435
436} // namespace detail
437
441
457template<typename T>
458class Task {
459public:
461 using promise_type = detail::TaskPromise<T>;
463 using value_type = T;
464
466 explicit Task() noexcept = default;
467
469
472 explicit Task(std::coroutine_handle<promise_type> coroutine) : mCoroutine(coroutine) {}
473
475 Task(const Task &) = delete;
477 Task &operator=(const Task &) = delete;
478
480 Task(Task &&other) noexcept : mCoroutine(other.mCoroutine) {
481 other.mCoroutine = nullptr;
482 }
483
485 Task &operator=(Task &&other) noexcept {
486 if (std::addressof(other) != this) {
487 if (mCoroutine) {
488 // The coroutine handle will be destroyed only after TaskFinalSuspend
489 if (mCoroutine.promise().setDestroyHandle()) {
490 mCoroutine.destroy();
491 }
492 }
493
494 mCoroutine = other.mCoroutine;
495 other.mCoroutine = nullptr;
496 }
497 return *this;
498 }
499
502 if (!mCoroutine)
503 return;
504
505 // The coroutine handle will be destroyed only after TaskFinalSuspend
506 if (mCoroutine.promise().setDestroyHandle()) {
507 mCoroutine.destroy();
508 }
509 };
510
512
516 bool isReady() const {
517 return !mCoroutine || mCoroutine.done();
518 }
519
521
527 auto operator co_await() const noexcept {
529 class TaskAwaiter : public detail::TaskAwaiterBase<promise_type> {
530 public:
531 TaskAwaiter(std::coroutine_handle<promise_type> awaitedCoroutine)
532 : detail::TaskAwaiterBase<promise_type>{awaitedCoroutine} {}
533
535 /*
536 * \return the result from the coroutine's promise, factically the
537 * value co_returned by the coroutine. */
538 auto await_resume() {
539 assert(this->mAwaitedCoroutine != nullptr);
540 if constexpr (!std::is_void_v<T>) {
541 return std::move(this->mAwaitedCoroutine.promise().result());
542 } else {
543 // Wil re-throw exception, if any is stored
544 this->mAwaitedCoroutine.promise().result();
545 }
546 }
547 };
548
549 return TaskAwaiter{mCoroutine};
550 }
551
553
568 template<typename ThenCallback>
569 requires(std::is_invocable_v<ThenCallback> ||
570 (!std::is_void_v<T> && std::is_invocable_v<ThenCallback, T>))
571 auto then(ThenCallback &&callback) {
572 // Provide a custom error handler that simply re-throws the current exception
573 return thenImpl(std::forward<ThenCallback>(callback), [](const auto &) { throw; });
574 }
575
576 template<typename ThenCallback, typename ErrorCallback>
577 requires((std::is_invocable_v<ThenCallback> ||
578 (!std::is_void_v<T> && std::is_invocable_v<ThenCallback, T>)) &&
579 std::is_invocable_v<ErrorCallback, const std::exception &>)
580 auto then(ThenCallback &&callback, ErrorCallback &&errorCallback) {
581 return thenImpl(std::forward<ThenCallback>(callback),
582 std::forward<ErrorCallback>(errorCallback));
583 }
584
585private:
586 template<typename ThenCallback, typename... Args>
587 auto invokeCb(ThenCallback &&callback, [[maybe_unused]] Args &&...args) {
588 if constexpr (std::is_invocable_v<ThenCallback, Args...>) {
589 return callback(std::forward<Args>(args)...);
590 } else {
591 return callback();
592 }
593 }
594
595 template<typename ThenCallback, typename Arg>
596 struct cb_invoke_result
597 : std::conditional_t<std::is_invocable_v<ThenCallback>, std::invoke_result<ThenCallback>,
598 std::invoke_result<ThenCallback, Arg>> {};
599
600 template<typename ThenCallback>
601 struct cb_invoke_result<ThenCallback, void> : std::invoke_result<ThenCallback> {};
602
603 template<typename ThenCallback, typename Arg>
604 using cb_invoke_result_t = typename cb_invoke_result<ThenCallback, Arg>::type;
605
606 template<typename R, typename ErrorCallback,
607 typename U = typename detail::isTask<R>::return_type>
608 auto handleException(ErrorCallback &errCb, const std::exception &exception) -> U {
609 errCb(exception);
610 if constexpr (!std::is_void_v<U>) {
611 return U{};
612 }
613 }
614
615 template<typename ThenCallback, typename ErrorCallback,
616 typename R = cb_invoke_result_t<ThenCallback, T>>
617 auto thenImpl(ThenCallback &&thenCallback, ErrorCallback &&errorCallback)
618 -> std::conditional_t<detail::isTask_v<R>, R, Task<R>> {
619 auto thenCb = std::forward<ThenCallback>(thenCallback);
620 auto errCb = std::forward<ErrorCallback>(errorCallback);
621 if constexpr (std::is_void_v<value_type>) {
622 try {
623 co_await *this;
624 } catch (const std::exception &e) {
625 co_return handleException<R>(errCb, e);
626 }
627 if constexpr (detail::isTask_v<R>) {
628 co_return co_await invokeCb(thenCb);
629 } else {
630 co_return invokeCb(thenCb);
631 }
632 } else {
633 std::optional<T> value;
634 try {
635 value = co_await *this;
636 } catch (const std::exception &e) {
637 co_return handleException<R>(errCb, e);
638 }
639 if constexpr (detail::isTask_v<R>) {
640 co_return co_await invokeCb(thenCb, std::move(*value));
641 } else {
642 co_return invokeCb(thenCb, std::move(*value));
643 }
644 }
645 }
646
647private:
649
653 std::coroutine_handle<promise_type> mCoroutine = {};
654};
655
656namespace detail {
657
658template<typename T>
659inline Task<T> TaskPromise<T>::get_return_object() noexcept {
660 return Task<T>{std::coroutine_handle<TaskPromise>::from_promise(*this)};
661}
662
663Task<void> inline TaskPromise<void>::get_return_object() noexcept {
664 return Task<void>{std::coroutine_handle<TaskPromise>::from_promise(*this)};
665}
666
667template<typename T>
668concept TaskConvertible = requires(T v, TaskPromiseBase t) {
669 { t.await_transform(v) };
670 };
671
672template<typename T>
673struct awaitable_return_type {
674 using type = std::decay_t<decltype(std::declval<T>().await_resume())>;
675};
676
677template<QCoro::detail::has_member_operator_coawait T>
678struct awaitable_return_type<T> {
679 using type = std::decay_t<
680 typename awaitable_return_type<decltype(std::declval<T>().operator co_await())>::type>;
681};
682
683template<QCoro::detail::has_nonmember_operator_coawait T>
684struct awaitable_return_type<T> {
685 using type = std::decay_t<
686 typename awaitable_return_type<decltype(operator co_await(std::declval<T>()))>::type>;
687};
688
689template<Awaitable Awaitable>
690using awaitable_return_type_t = typename detail::awaitable_return_type<Awaitable>::type;
691
692template<typename Awaitable>
693 requires TaskConvertible<Awaitable>
694using convertible_awaitable_return_type_t =
695 typename detail::awaitable_return_type<decltype(std::declval<TaskPromiseBase>().await_transform(
696 Awaitable()))>::type;
697
698template<typename Awaitable>
699 requires TaskConvertible<Awaitable>
700auto toTask(Awaitable &&future)
702 co_return co_await future;
703}
704
705struct WaitContext {
706 // QEventLoop loop;
707 bool coroutineFinished = false;
708 std::exception_ptr exception;
709};
710
711} // namespace detail
712
713template<typename T, typename QObjectSubclass, typename Callback>
714 requires detail::TaskConvertible<T> &&
715 (std::is_invocable_v<Callback> ||
716 std::is_invocable_v<Callback, detail::convertible_awaitable_return_type_t<T>> ||
717 std::is_invocable_v<Callback, QObjectSubclass *> ||
718 std::is_invocable_v<Callback, QObjectSubclass *,
719 detail::convertible_awaitable_return_type_t<T>>) &&
720 (!detail::isTask_v<T>)
721void connect(T &&future, QObjectSubclass *context, Callback func) {
722 auto task = detail::toTask(std::move(future));
723 connect(std::move(task), context, func);
724}
725
726} // namespace QCoro
An asynchronously executed task.
Definition qcorotask.h:458
T value_type
The type of the coroutine return value.
Definition qcorotask.h:463
auto then(ThenCallback &&callback, ErrorCallback &&errorCallback)
Definition qcorotask.h:580
~Task()
Destructor.
Definition qcorotask.h:501
Task(const Task &)=delete
Task cannot be copy-constructed.
Task & operator=(Task &&other) noexcept
The task can be move-assigned.
Definition qcorotask.h:485
Task() noexcept=default
Constructs a new empty task.
auto then(ThenCallback &&callback)
A callback to be invoked when the asynchronous task finishes.
Definition qcorotask.h:571
detail::TaskPromise< T > promise_type
Promise type of the coroutine. This is required by the C++ standard.
Definition qcorotask.h:461
bool isReady() const
Returns whether the task has finished.
Definition qcorotask.h:516
Task(Task &&other) noexcept
The task can be move-constructed.
Definition qcorotask.h:480
Task & operator=(const Task &)=delete
Task cannot be copy-assigned.
void connect(T &&future, QObjectSubclass *context, Callback func)
Definition qcorotask.h:721
Definition utils.h:161

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated by doxygen 1.9.8