QXmpp Version: 1.15.1
Loading...
Searching...
No Matches
Async.h
1// SPDX-FileCopyrightText: 2021 Linus Jahn <lnj@kaidan.im>
2// SPDX-FileCopyrightText: 2025 Filipe Azevedo <pasnox@gmail.com>
3//
4// SPDX-License-Identifier: LGPL-2.1-or-later
5
6#ifndef ASYNC_H
7#define ASYNC_H
8
9#include "QXmppAsync_p.h"
10#include "QXmppError.h"
11#include "QXmppGlobal.h"
12
13#include "Algorithms.h"
14
15#include <deque>
16#include <memory>
17#include <variant>
18
19#include <QFutureWatcher>
20
21namespace QXmpp::Private {
22
23template<typename Function>
24auto later(QObject *context, Function function)
25{
26 QMetaObject::invokeMethod(context, std::forward<Function>(function), Qt::QueuedConnection);
27}
28
29// QtFuture::makeReadyFuture() is deprecated since Qt 6.6 in favor of
30// makeReadyValueFuture() (available since Qt 6.6). Wrap it to stay
31// warning-free while keeping Qt 6.4 compatibility.
32#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
33template<typename T>
34auto makeReadyFuture(T &&value)
35{
36 return QtFuture::makeReadyValueFuture(std::forward<T>(value));
37}
38#else
39using QtFuture::makeReadyFuture;
40#endif
41
42template<typename T, typename Handler>
43void await(const QFuture<T> &future, QObject *context, Handler handler)
44{
45 auto *watcher = new QFutureWatcher<T>(context);
46 QObject::connect(watcher, &QFutureWatcherBase::finished,
47 context, [watcher, handler = std::move(handler)]() mutable {
48 handler(watcher->result());
49 watcher->deleteLater();
50 });
51 watcher->setFuture(future);
52}
53
54template<typename Handler>
55void await(const QFuture<void> &future, QObject *context, Handler handler)
56{
57 auto *watcher = new QFutureWatcher<void>(context);
58 QObject::connect(watcher, &QFutureWatcherBase::finished,
59 context, [watcher, handler = std::move(handler)]() mutable {
60 handler();
61 watcher->deleteLater();
62 });
63 watcher->setFuture(future);
64}
65
66template<typename T, typename Err, typename Function>
67auto mapSuccess(const std::variant<T, Err> &var, Function lambda)
68{
69 using MappedVariant = std::variant<std::invoke_result_t<Function, const T &>, Err>;
70 return map<MappedVariant>(lambda, var);
71}
72
73template<typename T, typename Err, typename Function>
74auto mapSuccess(std::variant<T, Err> &&var, Function lambda)
75{
76 using MappedVariant = std::variant<std::invoke_result_t<Function, T &&>, Err>;
77 return map<MappedVariant>(lambda, std::move(var));
78}
79
80template<typename T, typename Err>
81auto mapToSuccess(std::variant<T, Err> &&var)
82{
83 return mapSuccess(std::move(var), [](T) {
84 return Success();
85 });
86}
87
88// Creates a task for multiple tasks without a return value.
89template<typename T>
90QXmppTask<void> joinVoidTasks(QObject *context, std::vector<QXmppTask<T>> &&tasks)
91{
92 int taskCount = tasks.size();
93 auto finishedTaskCount = std::make_shared<int>();
94
95 auto promise = std::make_shared<QXmppPromise<void>>();
96 auto result = promise->task();
97
98 for (auto &task : tasks) {
99 task.then(context, [=]() mutable {
100 if (++(*finishedTaskCount) == taskCount) {
101 promise->finish();
102 }
103 });
104 }
105
106 return result;
107}
108
109template<typename Params, typename Response>
110struct AttachableRequests {
111 struct Request {
112 Params params;
113 std::vector<QXmppPromise<Response>> promises;
114 };
115
116 std::vector<Request> requests;
117
119 std::optional<QXmppTask<Response>> attach(const Params &key)
120 {
121 auto itr = std::ranges::find(requests, key, &Request::params);
122 if (itr != requests.end()) {
124 auto task = p.task();
125 itr->promises.push_back(std::move(p));
126 return task;
127 }
128
129 return std::nullopt;
130 }
131
132 QXmppTask<Response> makeNew(Params key)
133 {
134 Q_ASSERT(!contains(requests, key, &Request::params));
135
137 auto task = p.task();
138 requests.push_back(Request { key, { std::move(p) } });
139 return task;
140 }
141
142 void finish(const Params &key, Response &&response)
143 {
144 auto itr = std::ranges::find(requests, key, &Request::params);
145 Q_ASSERT(itr != requests.end());
146 if (itr == requests.end()) {
147 return;
148 }
149
150 auto promises = std::move(itr->promises);
151 requests.erase(itr);
152
153 for (auto it = promises.begin(); it != promises.end(); ++it) {
154 // copy unless this is the last iteration (then do move)
155 it->finish(std::next(it) == promises.end() ? std::move(response) : response);
156 }
157 }
158
159 QXmppTask<Response> produce(Params key, std::function<QXmppTask<Response>(Params)> requestFunction, QObject *context)
160 {
161 if (auto task = attach(key)) {
162 return std::move(*task);
163 }
164 auto task = makeNew(key);
165 requestFunction(key).then(context, [this, key](auto &&response) {
166 finish(key, std::move(response));
167 });
168 return task;
169 }
170};
171
172template<typename T>
173struct MultiPromise {
174 std::deque<QXmppPromise<T>> promises;
175
176 void finish(T &&response)
177 {
178 for (auto it = promises.begin(); it != promises.end(); ++it) {
179 // copy unless this is the last iteration (then do move)
180 it->finish(std::next(it) == promises.end() ? std::move(response) : response);
181 }
182 }
183 QXmppTask<T> generateTask()
184 {
185 promises.push_back(QXmppPromise<T>());
186 return promises.back().task();
187 }
188};
189
190template<>
191struct MultiPromise<void> {
192 std::vector<QXmppPromise<void>> promises;
193
194 void finish()
195 {
196 for (auto &p : promises) {
197 p.finish();
198 }
199 }
200 QXmppTask<void> generateTask()
201 {
202 promises.push_back(QXmppPromise<void>());
203 return promises.back().task();
204 }
205};
206
207} // namespace QXmpp::Private
208
209#endif // ASYNC_H
Create and update QXmppTask objects to communicate results of asynchronous operations.
Definition QXmppTask.h:81
QXmppTask< T > task()
Definition QXmppTask.h:167
void finish()
Definition QXmppTask.h:184
Definition QXmppTask.h:330