1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QFUTUREINTERFACE_H |
5 | #define QFUTUREINTERFACE_H |
6 | |
7 | #include <QtCore/qmutex.h> |
8 | #include <QtCore/qresultstore.h> |
9 | #ifndef QT_NO_EXCEPTIONS |
10 | #include <exception> |
11 | #endif |
12 | |
13 | #include <utility> |
14 | |
15 | QT_REQUIRE_CONFIG(future); |
16 | |
17 | QT_FORWARD_DECLARE_CLASS(QRunnable) |
18 | QT_FORWARD_DECLARE_CLASS(QException) |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | |
22 | template <typename T> class QFuture; |
23 | class QThreadPool; |
24 | class QFutureInterfaceBasePrivate; |
25 | class QFutureWatcherBase; |
26 | class QFutureWatcherBasePrivate; |
27 | |
28 | namespace QtPrivate { |
29 | template<typename Function, typename ResultType, typename ParentResultType> |
30 | class Continuation; |
31 | |
32 | class ExceptionStore; |
33 | |
34 | template<class Function, class ResultType> |
35 | class CanceledHandler; |
36 | |
37 | #ifndef QT_NO_EXCEPTIONS |
38 | template<class Function, class ResultType> |
39 | class FailureHandler; |
40 | #endif |
41 | |
42 | } |
43 | class QBasicFutureWatcher; |
44 | |
45 | class Q_CORE_EXPORT QFutureInterfaceBase |
46 | { |
47 | public: |
48 | enum State { |
49 | NoState = 0x00, |
50 | Running = 0x01, |
51 | Started = 0x02, |
52 | Finished = 0x04, |
53 | Canceled = 0x08, |
54 | Suspending = 0x10, |
55 | Suspended = 0x20, |
56 | Throttled = 0x40, |
57 | // Pending means that the future depends on another one, which is not finished yet |
58 | Pending = 0x80, |
59 | }; |
60 | |
61 | QFutureInterfaceBase(State initialState = NoState); |
62 | QFutureInterfaceBase(const QFutureInterfaceBase &other); |
63 | QFutureInterfaceBase(QFutureInterfaceBase &&other) noexcept |
64 | : d(std::exchange(obj&: other.d, new_val: nullptr)) {} |
65 | QFutureInterfaceBase &operator=(const QFutureInterfaceBase &other); |
66 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QFutureInterfaceBase) |
67 | virtual ~QFutureInterfaceBase(); |
68 | |
69 | // reporting functions available to the engine author: |
70 | void reportStarted(); |
71 | void reportFinished(); |
72 | void reportCanceled(); |
73 | #ifndef QT_NO_EXCEPTIONS |
74 | void reportException(const QException &e); |
75 | #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) |
76 | void reportException(std::exception_ptr e); |
77 | #else |
78 | void reportException(const std::exception_ptr &e); |
79 | #endif |
80 | #endif |
81 | void reportResultsReady(int beginIndex, int endIndex); |
82 | |
83 | void setRunnable(QRunnable *runnable); |
84 | void setThreadPool(QThreadPool *pool); |
85 | QThreadPool *threadPool() const; |
86 | void setFilterMode(bool enable); |
87 | void setProgressRange(int minimum, int maximum); |
88 | int progressMinimum() const; |
89 | int progressMaximum() const; |
90 | bool isProgressUpdateNeeded() const; |
91 | void setProgressValue(int progressValue); |
92 | int progressValue() const; |
93 | void setProgressValueAndText(int progressValue, const QString &progressText); |
94 | QString progressText() const; |
95 | |
96 | void setExpectedResultCount(int resultCount); |
97 | int expectedResultCount(); |
98 | int resultCount() const; |
99 | |
100 | bool queryState(State state) const; |
101 | bool isRunning() const; |
102 | bool isStarted() const; |
103 | bool isCanceled() const; |
104 | bool isFinished() const; |
105 | #if QT_DEPRECATED_SINCE(6, 0) |
106 | QT_DEPRECATED_VERSION_X_6_0("Use isSuspending() or isSuspended() instead." ) |
107 | bool isPaused() const; |
108 | |
109 | QT_DEPRECATED_VERSION_X_6_0("Use setSuspended() instead." ) |
110 | void setPaused(bool paused) { setSuspended(paused); } |
111 | |
112 | QT_DEPRECATED_VERSION_X_6_0("Use toggleSuspended() instead." ) |
113 | void togglePaused() { toggleSuspended(); } |
114 | #endif |
115 | bool isSuspending() const; |
116 | bool isSuspended() const; |
117 | bool isThrottled() const; |
118 | bool isResultReadyAt(int index) const; |
119 | bool isValid() const; |
120 | int loadState() const; |
121 | |
122 | void cancel(); |
123 | void cancelAndFinish() { cancel(mode: CancelMode::CancelAndFinish); } |
124 | |
125 | void setSuspended(bool suspend); |
126 | void toggleSuspended(); |
127 | void reportSuspended() const; |
128 | void setThrottled(bool enable); |
129 | |
130 | void waitForFinished(); |
131 | bool waitForNextResult(); |
132 | void waitForResult(int resultIndex); |
133 | void waitForResume(); |
134 | void suspendIfRequested(); |
135 | |
136 | QMutex &mutex() const; |
137 | bool hasException() const; |
138 | QtPrivate::ExceptionStore &exceptionStore(); |
139 | QtPrivate::ResultStoreBase &resultStoreBase(); |
140 | const QtPrivate::ResultStoreBase &resultStoreBase() const; |
141 | |
142 | inline bool operator==(const QFutureInterfaceBase &other) const { return d == other.d; } |
143 | inline bool operator!=(const QFutureInterfaceBase &other) const { return d != other.d; } |
144 | |
145 | // ### Qt 7: inline |
146 | void swap(QFutureInterfaceBase &other) noexcept; |
147 | |
148 | template<typename T> |
149 | static QFutureInterfaceBase get(const QFuture<T> &future); // implemented in qfuture.h |
150 | |
151 | bool isChainCanceled() const; |
152 | |
153 | protected: |
154 | // ### Qt 7: remove const from refT/derefT |
155 | bool refT() const noexcept; |
156 | bool derefT() const noexcept; |
157 | void reset(); |
158 | void rethrowPossibleException(); |
159 | public: |
160 | |
161 | #ifndef QFUTURE_TEST |
162 | private: |
163 | #endif |
164 | QFutureInterfaceBasePrivate *d; |
165 | |
166 | private: |
167 | friend class QFutureWatcherBase; |
168 | friend class QFutureWatcherBasePrivate; |
169 | |
170 | template<typename Function, typename ResultType, typename ParentResultType> |
171 | friend class QtPrivate::Continuation; |
172 | |
173 | template<class Function, class ResultType> |
174 | friend class QtPrivate::CanceledHandler; |
175 | |
176 | #ifndef QT_NO_EXCEPTIONS |
177 | template<class Function, class ResultType> |
178 | friend class QtPrivate::FailureHandler; |
179 | #endif |
180 | |
181 | friend class QBasicFutureWatcher; |
182 | |
183 | template<class T> |
184 | friend class QPromise; |
185 | |
186 | protected: |
187 | void setContinuation(std::function<void(const QFutureInterfaceBase &)> func); |
188 | void setContinuation(std::function<void(const QFutureInterfaceBase &)> func, |
189 | QFutureInterfaceBasePrivate *continuationFutureData); |
190 | void cleanContinuation(); |
191 | void runContinuation() const; |
192 | |
193 | void setLaunchAsync(bool value); |
194 | bool launchAsync() const; |
195 | |
196 | bool isRunningOrPending() const; |
197 | |
198 | enum class CancelMode { CancelOnly, CancelAndFinish }; |
199 | void cancel(CancelMode mode); |
200 | }; |
201 | |
202 | inline void swap(QFutureInterfaceBase &lhs, QFutureInterfaceBase &rhs) noexcept |
203 | { |
204 | lhs.swap(other&: rhs); |
205 | } |
206 | |
207 | template <typename T> |
208 | class QFutureInterface : public QFutureInterfaceBase |
209 | { |
210 | public: |
211 | QFutureInterface(State initialState = NoState) |
212 | : QFutureInterfaceBase(initialState) |
213 | { |
214 | refT(); |
215 | } |
216 | QFutureInterface(const QFutureInterface &other) |
217 | : QFutureInterfaceBase(other) |
218 | { |
219 | refT(); |
220 | } |
221 | QFutureInterface(const QFutureInterfaceBase &dd) : QFutureInterfaceBase(dd) { refT(); } |
222 | QFutureInterface(QFutureInterfaceBase &&dd) noexcept : QFutureInterfaceBase(std::move(dd)) { refT(); } |
223 | QFutureInterface &operator=(const QFutureInterface &other) |
224 | { |
225 | QFutureInterface copy(other); |
226 | swap(other&: copy); |
227 | return *this; |
228 | } |
229 | QFutureInterface(QFutureInterface &&other) = default; |
230 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QFutureInterface) |
231 | |
232 | ~QFutureInterface() |
233 | { |
234 | if (!derefT() && !hasException()) |
235 | resultStoreBase().template clear<T>(); |
236 | } |
237 | |
238 | static QFutureInterface canceledResult() |
239 | { return QFutureInterface(State(Started | Finished | Canceled)); } |
240 | |
241 | inline QFuture<T> future(); // implemented in qfuture.h |
242 | |
243 | template <typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true> |
244 | inline bool reportAndEmplaceResult(int index, Args&&...args); |
245 | inline bool reportResult(const T *result, int index = -1); |
246 | inline bool reportAndMoveResult(T &&result, int index = -1); |
247 | inline bool reportResult(T &&result, int index = -1); |
248 | inline bool reportResult(const T &result, int index = -1); |
249 | inline bool reportResults(const QList<T> &results, int beginIndex = -1, int count = -1); |
250 | inline bool reportFinished(const T *result); |
251 | void reportFinished() |
252 | { |
253 | QFutureInterfaceBase::reportFinished(); |
254 | QFutureInterfaceBase::runContinuation(); |
255 | } |
256 | |
257 | inline const T &resultReference(int index) const; |
258 | inline const T *resultPointer(int index) const; |
259 | inline QList<T> results(); |
260 | |
261 | T takeResult(); |
262 | #if 0 |
263 | // TODO: Enable and make it return a QList, when QList is fixed to support move-only types |
264 | std::vector<T> takeResults(); |
265 | #endif |
266 | |
267 | #ifndef QT_NO_EXCEPTIONS |
268 | void reportException(const std::exception_ptr &e) |
269 | { |
270 | if (hasException()) |
271 | return; |
272 | |
273 | resultStoreBase().template clear<T>(); |
274 | QFutureInterfaceBase::reportException(e); |
275 | } |
276 | void reportException(const QException &e) |
277 | { |
278 | if (hasException()) |
279 | return; |
280 | |
281 | resultStoreBase().template clear<T>(); |
282 | QFutureInterfaceBase::reportException(e); |
283 | } |
284 | #endif |
285 | }; |
286 | |
287 | template <typename T> |
288 | inline bool QFutureInterface<T>::reportResult(const T *result, int index) |
289 | { |
290 | QMutexLocker<QMutex> locker{&mutex()}; |
291 | if (this->queryState(Canceled) || this->queryState(Finished)) |
292 | return false; |
293 | |
294 | Q_ASSERT(!hasException()); |
295 | QtPrivate::ResultStoreBase &store = resultStoreBase(); |
296 | |
297 | const int resultCountBefore = store.count(); |
298 | const int insertIndex = store.addResult<T>(index, result); |
299 | if (insertIndex == -1) |
300 | return false; |
301 | if (store.filterMode()) { |
302 | this->reportResultsReady(resultCountBefore, store.count()); |
303 | } else { |
304 | this->reportResultsReady(insertIndex, insertIndex + 1); |
305 | } |
306 | return true; |
307 | } |
308 | |
309 | template<typename T> |
310 | template<typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool>> |
311 | bool QFutureInterface<T>::reportAndEmplaceResult(int index, Args&&...args) |
312 | { |
313 | QMutexLocker<QMutex> locker{&mutex()}; |
314 | if (queryState(state: Canceled) || queryState(state: Finished)) |
315 | return false; |
316 | |
317 | Q_ASSERT(!hasException()); |
318 | QtPrivate::ResultStoreBase &store = resultStoreBase(); |
319 | |
320 | const int oldResultCount = store.count(); |
321 | const int insertIndex = store.emplaceResult<T>(index, std::forward<Args>(args)...); |
322 | // Let's make sure it's not in pending results. |
323 | if (insertIndex != -1 && (!store.filterMode() || oldResultCount < store.count())) |
324 | reportResultsReady(beginIndex: insertIndex, endIndex: store.count()); |
325 | return insertIndex != -1; |
326 | } |
327 | |
328 | template<typename T> |
329 | bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index) |
330 | { |
331 | return reportAndEmplaceResult(index, std::move(result)); |
332 | } |
333 | |
334 | template<typename T> |
335 | bool QFutureInterface<T>::reportResult(T &&result, int index) |
336 | { |
337 | return reportAndMoveResult(result: std::move(result), index); |
338 | } |
339 | |
340 | template <typename T> |
341 | inline bool QFutureInterface<T>::reportResult(const T &result, int index) |
342 | { |
343 | return reportResult(&result, index); |
344 | } |
345 | |
346 | template<typename T> |
347 | inline bool QFutureInterface<T>::reportResults(const QList<T> &_results, int beginIndex, int count) |
348 | { |
349 | QMutexLocker<QMutex> locker{&mutex()}; |
350 | if (this->queryState(Canceled) || this->queryState(Finished)) |
351 | return false; |
352 | |
353 | Q_ASSERT(!hasException()); |
354 | auto &store = resultStoreBase(); |
355 | |
356 | const int resultCountBefore = store.count(); |
357 | const int insertIndex = store.addResults(beginIndex, &_results, count); |
358 | if (insertIndex == -1) |
359 | return false; |
360 | if (store.filterMode()) { |
361 | this->reportResultsReady(resultCountBefore, store.count()); |
362 | } else { |
363 | this->reportResultsReady(insertIndex, insertIndex + _results.size()); |
364 | } |
365 | return true; |
366 | } |
367 | |
368 | template <typename T> |
369 | inline bool QFutureInterface<T>::reportFinished(const T *result) |
370 | { |
371 | bool resultReported = false; |
372 | if (result) |
373 | resultReported = reportResult(result); |
374 | reportFinished(); |
375 | return resultReported; |
376 | } |
377 | |
378 | template <typename T> |
379 | inline const T &QFutureInterface<T>::resultReference(int index) const |
380 | { |
381 | Q_ASSERT(!hasException()); |
382 | |
383 | QMutexLocker<QMutex> locker{&mutex()}; |
384 | return resultStoreBase().resultAt(index).template value<T>(); |
385 | } |
386 | |
387 | template <typename T> |
388 | inline const T *QFutureInterface<T>::resultPointer(int index) const |
389 | { |
390 | Q_ASSERT(!hasException()); |
391 | |
392 | QMutexLocker<QMutex> locker{&mutex()}; |
393 | return resultStoreBase().resultAt(index).template pointer<T>(); |
394 | } |
395 | |
396 | template <typename T> |
397 | inline QList<T> QFutureInterface<T>::results() |
398 | { |
399 | if (this->isCanceled()) { |
400 | rethrowPossibleException(); |
401 | return QList<T>(); |
402 | } |
403 | |
404 | QFutureInterfaceBase::waitForResult(resultIndex: -1); |
405 | |
406 | QList<T> res; |
407 | QMutexLocker<QMutex> locker{&mutex()}; |
408 | |
409 | QtPrivate::ResultIteratorBase it = resultStoreBase().begin(); |
410 | while (it != resultStoreBase().end()) { |
411 | res.append(it.value<T>()); |
412 | ++it; |
413 | } |
414 | |
415 | return res; |
416 | } |
417 | |
418 | template<typename T> |
419 | T QFutureInterface<T>::takeResult() |
420 | { |
421 | Q_ASSERT(isValid()); |
422 | |
423 | // Note: we wait for all, this is intentional, |
424 | // not to mess with other unready results. |
425 | waitForResult(resultIndex: -1); |
426 | |
427 | Q_ASSERT(!hasException()); |
428 | |
429 | const QMutexLocker<QMutex> locker{&mutex()}; |
430 | QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0); |
431 | T ret(std::move_if_noexcept(position.value<T>())); |
432 | reset(); |
433 | resultStoreBase().template clear<T>(); |
434 | |
435 | return ret; |
436 | } |
437 | |
438 | #if 0 |
439 | template<typename T> |
440 | std::vector<T> QFutureInterface<T>::takeResults() |
441 | { |
442 | Q_ASSERT(isValid()); |
443 | |
444 | waitForResult(-1); |
445 | |
446 | Q_ASSERT(!hasException()); |
447 | |
448 | std::vector<T> res; |
449 | res.reserve(resultCount()); |
450 | |
451 | const QMutexLocker<QMutex> locker{&mutex()}; |
452 | |
453 | QtPrivate::ResultIteratorBase it = resultStoreBase().begin(); |
454 | for (auto endIt = resultStoreBase().end(); it != endIt; ++it) |
455 | res.push_back(std::move_if_noexcept(it.value<T>())); |
456 | |
457 | reset(); |
458 | resultStoreBase().template clear<T>(); |
459 | |
460 | return res; |
461 | } |
462 | #endif |
463 | |
464 | template <> |
465 | class QFutureInterface<void> : public QFutureInterfaceBase |
466 | { |
467 | public: |
468 | explicit QFutureInterface<void>(State initialState = NoState) |
469 | : QFutureInterfaceBase(initialState) |
470 | { } |
471 | |
472 | QFutureInterface(const QFutureInterfaceBase &dd) : QFutureInterfaceBase(dd) { } |
473 | |
474 | static QFutureInterface<void> canceledResult() |
475 | { return QFutureInterface(State(Started | Finished | Canceled)); } |
476 | |
477 | |
478 | inline QFuture<void> future(); // implemented in qfuture.h |
479 | |
480 | bool reportResult(const void *, int) { return false; } |
481 | bool reportResults(const QList<void> &, int) { return false; } |
482 | bool reportFinished(const void *) |
483 | { |
484 | reportFinished(); |
485 | return false; |
486 | } |
487 | void reportFinished() |
488 | { |
489 | QFutureInterfaceBase::reportFinished(); |
490 | QFutureInterfaceBase::runContinuation(); |
491 | } |
492 | }; |
493 | |
494 | template<typename T> |
495 | inline void swap(QFutureInterface<T> &a, QFutureInterface<T> &b) noexcept |
496 | { |
497 | a.swap(b); |
498 | } |
499 | |
500 | QT_END_NAMESPACE |
501 | |
502 | #endif // QFUTUREINTERFACE_H |
503 | |