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