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