21#include <quentier/utility/Linkage.h>
23#include <QAbstractEventDispatcher>
25#include <QFutureWatcher>
27#include <QMutexLocker>
31#include <quentier/threading/QtFutureContinuations.h>
33#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
37#include <quentier/threading/Qt5Promise.h>
46namespace quentier::threading {
52[[nodiscard]] std::enable_if_t<
53 std::negation_v<std::is_same<std::decay_t<T>,
void>>,
54 QFuture<std::decay_t<T>>>
58 QFuture<std::decay_t<T>> future = promise.future();
61 promise.addResult(std::move(t));
67[[nodiscard]] QFuture<void> QUENTIER_EXPORT makeReadyFuture();
73template <
class T,
class E>
74[[nodiscard]] std::enable_if_t<std::is_base_of_v<QException, E>, QFuture<T>>
75 makeExceptionalFuture(
const E & e)
78 QFuture<std::decay_t<T>> future = promise.future();
81 promise.setException(e);
87#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
93[[nodiscard]] QFuture<T> makeExceptionalFuture(std::exception_ptr e)
96 QFuture<std::decay_t<T>> future = promise.future();
99 promise.setException(std::move(e));
110template <
class T,
class U>
111void bindCancellation(
const QFuture<T> & from, QFuture<U> to)
113 auto watcher = std::make_unique<QFutureWatcher<T>>();
114 auto * rawWatcher = watcher.get();
117 rawWatcher, &QFutureWatcher<T>::canceled, rawWatcher,
118 [rawWatcher, to]()
mutable {
120 rawWatcher->deleteLater();
124 rawWatcher, &QFutureWatcher<T>::finished, rawWatcher,
125 [rawWatcher] { rawWatcher->deleteLater(); });
127 watcher->setFuture(from);
128 Q_UNUSED(watcher.release());
138[[nodiscard]] QFuture<void> QUENTIER_EXPORT
139 whenAll(QList<QFuture<void>> futures);
151[[nodiscard]] std::enable_if_t<
152 !std::is_void_v<std::decay_t<T>>, QFuture<QList<std::decay_t<T>>>>
153 whenAll(QList<QFuture<std::decay_t<T>>> futures)
155 if (Q_UNLIKELY(futures.isEmpty())) {
156 return makeReadyFuture<QList<std::decay_t<T>>>({});
159 auto promise = std::make_shared<QPromise<QList<std::decay_t<T>>>>();
160 auto future = promise->future();
162 for (
auto & f: futures) {
163 threading::bindCancellation(future, f);
166 const auto totalItemCount = futures.size();
167 promise->setProgressRange(0,
static_cast<int>(totalItemCount));
168 promise->setProgressValue(0);
172 auto resultIndexedList =
173 std::make_shared<QList<std::pair<int, std::decay_t<T>>>>();
175 auto processedItemsCount = std::make_shared<int>(0);
176 auto exceptionFlag = std::make_shared<bool>(
false);
177 auto mutex = std::make_shared<QMutex>();
179 for (
int i = 0; i < futures.size(); ++i) {
180 auto & f = futures[i];
181 auto thenFuture = then(
183 [promise, processedItemsCount, totalItemCount, exceptionFlag, mutex,
184 resultIndexedList, i](std::decay_t<T> result) {
185 if (promise->isCanceled()) {
191 const QMutexLocker locker{mutex.get()};
193 if (*exceptionFlag) {
197 ++(*processedItemsCount);
198 count = *processedItemsCount;
199 promise->setProgressValue(count);
201 resultIndexedList->append(
202 std::make_pair(i, std::move(result)));
205 if (count == totalItemCount) {
207 resultIndexedList->begin(), resultIndexedList->end(),
208 [](
const auto & lhs,
const auto & rhs) {
209 return lhs.first < rhs.first;
213 std::make_shared<QList<std::decay_t<T>>>();
214 resultList->reserve(resultIndexedList->size());
215 for (
auto & [i, v]: *resultIndexedList) {
216 resultList->append(std::move(v));
219 promise->addResult(*resultList);
225 std::move(thenFuture),
226 [promise, mutex, exceptionFlag](
const QException & e) {
227 if (promise->isCanceled()) {
232 const QMutexLocker locker{mutex.get()};
234 if (*exceptionFlag) {
238 *exceptionFlag =
true;
241 promise->setException(e);
254template <
class T,
class U>
255void mapFutureProgress(
256 const QFuture<T> & future,
const std::shared_ptr<
QPromise<U>> & promise)
258 const auto futureProgressMinimum = future.progressMinimum();
259 const auto futureProgressRange =
260 future.progressMaximum() - futureProgressMinimum;
262 Q_ASSERT(futureProgressRange >= 0);
264 const auto promiseFuture = promise->future();
265 const auto promiseProgressMinimum = promiseFuture.progressMinimum();
266 const auto promiseProgressMaximum = promiseFuture.progressMaximum();
268 const auto promiseProgressRange =
269 promiseProgressMaximum - promiseProgressMinimum;
271 Q_ASSERT(promiseProgressRange >= 0);
273 auto futureWatcher = std::make_unique<QFutureWatcher<T>>();
276 futureWatcher.get(), &QFutureWatcher<T>::progressValueChanged,
278 [promise, futureProgressMinimum, futureProgressRange,
279 promiseProgressRange, promiseProgressMinimum,
280 promiseProgressMaximum](
int progressValue) {
281 if (Q_UNLIKELY(futureProgressRange == 0)) {
282 promise->setProgressValue(0);
286 const auto progressPart =
287 static_cast<double>(progressValue - futureProgressMinimum) /
288 static_cast<double>(futureProgressRange);
290 const auto mappedProgressValue =
static_cast<int>(
291 std::round(progressPart * promiseProgressRange));
293 promise->setProgressValue(std::clamp(
294 promiseProgressMinimum + mappedProgressValue,
295 promiseProgressMinimum, promiseProgressMaximum));
299 futureWatcher.get(), &QFutureWatcher<T>::finished, futureWatcher.get(),
300 [futureWatcherWeak = QPointer<QFutureWatcher<T>>(futureWatcher.get())] {
301 if (!futureWatcherWeak.isNull()) {
302 futureWatcherWeak->deleteLater();
307 futureWatcher.get(), &QFutureWatcher<T>::canceled, futureWatcher.get(),
308 [futureWatcherWeak = QPointer<QFutureWatcher<T>>(futureWatcher.get())] {
309 if (!futureWatcherWeak.isNull()) {
310 futureWatcherWeak->deleteLater();
314 futureWatcher->setFuture(future);
315 Q_UNUSED(futureWatcher.release());
Definition Qt5Promise.h:28