Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qfutureinterface.cpp
Go to the documentation of this file.
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// qfutureinterface.h included from qfuture.h
5#include "qfuture.h"
7
8#include <QtCore/qatomic.h>
9#include <QtCore/qthread.h>
10#include <QtCore/qvarlengtharray.h>
11#include <QtCore/private/qsimd_p.h> // for qYieldCpu()
12#include <private/qthreadpool_p.h>
13
14#ifdef interface
15# undef interface
16#endif
17
18// GCC 12 gets confused about QFutureInterfaceBase::state, for some non-obvious
19// reason
20// warning: ‘unsigned int __atomic_or_fetch_4(volatile void*, unsigned int, int)’ writing 4 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
21QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
22
24
25enum {
27};
28
29namespace {
30class ThreadPoolThreadReleaser {
31 QThreadPool *m_pool;
32public:
33 explicit ThreadPoolThreadReleaser(QThreadPool *pool)
34 : m_pool(pool)
35 { if (pool) pool->releaseThread(); }
36 ~ThreadPoolThreadReleaser()
37 { if (m_pool) m_pool->reserveThread(); }
38};
39
40const auto suspendingOrSuspended =
42
43} // unnamed namespace
44
46 = default;
47
49
51 : d(new QFutureInterfaceBasePrivate(initialState))
52{ }
53
55 : d(other.d)
56{
57 d->refCount.ref();
58}
59
61{
62 if (d && !d->refCount.deref())
63 delete d;
64}
65
66static inline int switch_on(QAtomicInt &a, int which)
67{
68 return a.fetchAndOrRelaxed(which) | which;
69}
70
71static inline int switch_off(QAtomicInt &a, int which)
72{
73 return a.fetchAndAndRelaxed(~which) & ~which;
74}
75
76static inline int switch_from_to(QAtomicInt &a, int from, int to)
77{
78 const auto adjusted = [&](int old) { return (old & ~from) | to; };
79 int value = a.loadRelaxed();
80 while (!a.testAndSetRelaxed(value, adjusted(value), value))
81 qYieldCpu();
82 return value;
83}
84
86{
88}
89
91{
92 QMutexLocker locker(&d->m_mutex);
93
94 const auto oldState = d->state.loadRelaxed();
95
96 switch (mode) {
98 if ((oldState & Finished) && (oldState & Canceled))
99 return;
100 switch_from_to(d->state, suspendingOrSuspended | Running, Canceled | Finished);
101 break;
103 if (oldState & Canceled)
104 return;
105 switch_from_to(d->state, suspendingOrSuspended, Canceled);
106 break;
107 }
108
109 // Cancel the continuations chain
111 while (next) {
112 next->continuationState = QFutureInterfaceBasePrivate::Canceled;
113 next = next->continuationData;
114 }
115
118
119 if (!(oldState & Canceled))
121 if (mode == CancelMode::CancelAndFinish && !(oldState & Finished))
123
124 d->isValid = false;
125}
126
128{
129 QMutexLocker locker(&d->m_mutex);
130 if (suspend) {
133 } else {
134 switch_off(d->state, suspendingOrSuspended);
137 }
138}
139
141{
142 QMutexLocker locker(&d->m_mutex);
143 if (d->state.loadRelaxed() & suspendingOrSuspended) {
144 switch_off(d->state, suspendingOrSuspended);
147 } else {
150 }
151}
152
154{
155 // Needs to be called when pause is in effect,
156 // i.e. no more events will be reported.
157
158 QMutexLocker locker(&d->m_mutex);
159 const int state = d->state.loadRelaxed();
160 if (!(state & Suspending) || (state & Suspended))
161 return;
162
165}
166
168{
170 if (enable) {
172 } else {
174 if (!(d->state.loadRelaxed() & suspendingOrSuspended))
176 }
177}
178
179
181{
182 return queryState(Running);
183}
184
186{
187 return queryState(Started);
188}
189
191{
192 return queryState(Canceled);
193}
194
196{
197 return queryState(Finished);
198}
199
201{
202 return queryState(Suspending);
203}
204
205#if QT_DEPRECATED_SINCE(6, 0)
206bool QFutureInterfaceBase::isPaused() const
207{
208 return queryState(static_cast<State>(suspendingOrSuspended));
209}
210#endif
211
213{
214 return queryState(Suspended);
215}
216
218{
219 return queryState(Throttled);
220}
221
223{
226}
227
229{
230 const QMutexLocker lock(&d->m_mutex);
231 return d->isValid;
232}
233
235{
236 return queryState(static_cast<State>(Running | Pending));
237}
238
240{
242 return d->internal_waitForNextResult();
243}
244
246{
247 // return early if possible to avoid taking the mutex lock.
248 {
249 const int state = d->state.loadRelaxed();
250 if (!(state & suspendingOrSuspended) || (state & Canceled))
251 return;
252 }
253
255 const int state = d->state.loadRelaxed();
256 if (!(state & suspendingOrSuspended) || (state & Canceled))
257 return;
258
259 // decrease active thread count since this thread will wait.
260 const ThreadPoolThreadReleaser releaser(d->pool());
261
263}
264
266{
267 const auto canSuspend = [] (int state) {
268 // can suspend only if 1) in any suspend-related state; 2) not canceled
269 return (state & suspendingOrSuspended) && !(state & Canceled);
270 };
271
272 // return early if possible to avoid taking the mutex lock.
273 {
274 const int state = d->state.loadRelaxed();
275 if (!canSuspend(state))
276 return;
277 }
278
280 const int state = d->state.loadRelaxed();
281 if (!canSuspend(state))
282 return;
283
284 // Note: expecting that Suspending and Suspended are mutually exclusive
285 if (!(state & Suspended)) {
286 // switch state in case this is the first invocation
289 }
290
291 // decrease active thread count since this thread will wait.
292 const ThreadPoolThreadReleaser releaser(d->pool());
294}
295
297{
298 const QMutexLocker lock(&d->m_mutex);
299 return d->m_progressValue;
300}
301
303{
304 const QMutexLocker lock(&d->m_mutex);
305 return d->m_progress ? d->m_progress->minimum : 0;
306}
307
309{
310 const QMutexLocker lock(&d->m_mutex);
311 return d->m_progress ? d->m_progress->maximum : 0;
312}
313
315{
317 return d->internal_resultCount();
318}
319
321{
322 QMutexLocker locker(&d->m_mutex);
323 return d->m_progress ? d->m_progress->text : QString();
324}
325
327{
328 QMutexLocker locker(&d->m_mutex);
329 return !d->progressTime.isValid() || (d->progressTime.elapsed() > (1000 / MaxProgressEmitsPerSecond));
330}
331
333{
334 QMutexLocker locker(&d->m_mutex);
336 return;
339 d->isValid = true;
340}
341
343{
344 cancel();
345}
346
347#ifndef QT_NO_EXCEPTIONS
349{
350 try {
351 exception.raise();
352 } catch (...) {
353 reportException(std::current_exception());
354 }
355}
356
357#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
358void QFutureInterfaceBase::reportException(std::exception_ptr exception)
359#else
360void QFutureInterfaceBase::reportException(const std::exception_ptr &exception)
361#endif
362{
363 QMutexLocker locker(&d->m_mutex);
364 if (d->state.loadRelaxed() & (Canceled|Finished))
365 return;
366
367 d->hasException = true;
368 d->data.setException(exception);
369 switch_on(d->state, Canceled);
370 d->waitCondition.wakeAll();
371 d->pausedWaitCondition.wakeAll();
373}
374#endif
375
377{
378 QMutexLocker locker(&d->m_mutex);
379 if (!isFinished()) {
383 }
384}
385
387{
388 if (d->m_progress)
391}
392
394{
395 return d->m_expectedResultCount;
396}
397
399{
400 return d->state.loadRelaxed() & state;
401}
402
404{
405 // Used from ~QPromise, so this check is needed
406 if (!d)
408 return d->state.loadRelaxed();
409}
410
412{
413 if (d->hasException)
415
417 if (!isRunningOrPending())
418 return;
419 lock.unlock();
420
421 // To avoid deadlocks and reduce the number of threads used, try to
422 // run the runnable in the current thread.
423 d->pool()->d_func()->stealAndRunRunnable(d->runnable);
424
425 lock.relock();
426
427 const int waitIndex = (resultIndex == -1) ? INT_MAX : resultIndex;
428 while (isRunningOrPending() && !d->internal_isResultReadyAt(waitIndex))
429 d->waitCondition.wait(&d->m_mutex);
430
431 if (d->hasException)
433}
434
436{
438 const bool alreadyFinished = isFinished();
439 lock.unlock();
440
441 if (!alreadyFinished) {
442 d->pool()->d_func()->stealAndRunRunnable(d->runnable);
443
444 lock.relock();
445
446 while (!isFinished())
447 d->waitCondition.wait(&d->m_mutex);
448 }
449
450 if (d->hasException)
452}
453
454void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex)
455{
456 if (beginIndex == endIndex || (d->state.loadRelaxed() & (Canceled|Finished)))
457 return;
458
460
461 if (!d->m_progress) {
462 if (d->internal_updateProgressValue(d->m_progressValue + endIndex - beginIndex) == false) {
464 beginIndex,
465 endIndex));
466 return;
467 }
468
471 QString()),
473 beginIndex,
474 endIndex));
475 return;
476 }
478}
479
481{
482 d->runnable = runnable;
483}
484
486{
487 d->m_pool = pool;
488}
489
491{
492 return d->m_pool;
493}
494
496{
497 QMutexLocker locker(&d->m_mutex);
498 if (!hasException())
500}
501
518void QFutureInterfaceBase::setProgressRange(int minimum, int maximum)
519{
520 QMutexLocker locker(&d->m_mutex);
521 if (!d->m_progress)
523 d->m_progress->minimum = minimum;
524 d->m_progress->maximum = qMax(minimum, maximum);
526 d->m_progressValue = minimum;
527}
528
530{
532}
533
541 const QString &progressText)
542{
543 QMutexLocker locker(&d->m_mutex);
544 if (!d->m_progress)
546
547 const bool useProgressRange = (d->m_progress->maximum != 0) || (d->m_progress->minimum != 0);
548 if (useProgressRange
549 && ((progressValue < d->m_progress->minimum) || (progressValue > d->m_progress->maximum))) {
550 return;
551 }
552
554 return;
555
556 if (d->state.loadRelaxed() & (Canceled|Finished))
557 return;
558
562 d->m_progress->text));
563 }
564}
565
567{
568 return d->m_mutex;
569}
570
572{
573 return d->hasException;
574}
575
577{
579 return d->data.m_exceptionStore;
580}
581
583{
585 return d->data.m_results;
586}
587
589{
591 return d->data.m_results;
592}
593
595{
597 swap(copy);
598 return *this;
599}
600
601// ### Qt 7: inline
603{
604 qSwap(d, other.d);
605}
606
607bool QFutureInterfaceBase::refT() const noexcept
608{
609 return d->refCount.refT();
610}
611
612bool QFutureInterfaceBase::derefT() const noexcept
613{
614 // Called from ~QFutureInterface
615 return !d || d->refCount.derefT();
616}
617
619{
620 d->m_progressValue = 0;
621 d->m_progress.reset();
623 d->isValid = false;
624}
625
627{
628 if (hasException())
630}
631
633 : state(initialState)
634{
636}
637
639{
640 if (hasException)
641 data.m_exceptionStore.~ExceptionStore();
642 else
643 data.m_results.~ResultStoreBase();
644}
645
647{
648 return hasException ? 0 : data.m_results.count(); // ### subtract canceled results.
649}
650
652{
653 return hasException ? false : (data.m_results.contains(index));
654}
655
657{
658 if (hasException)
659 return false;
660
661 if (data.m_results.hasNextResult())
662 return true;
663
665 && data.m_results.hasNextResult() == false)
667
669 && data.m_results.hasNextResult();
670}
671
673{
674 if (m_progressValue >= progress)
675 return false;
676
677 m_progressValue = progress;
678
679 if (progressTime.isValid() && m_progressValue != 0) // make sure the first and last steps are emitted.
681 return false;
682
684 return true;
685
686}
687
689 const QString &progressText)
690{
691 if (m_progressValue >= progress)
692 return false;
693
695
696 m_progressValue = progress;
697 m_progress->text = progressText;
698
699 if (progressTime.isValid() && m_progressValue != m_progress->maximum) // make sure the first and last steps are emitted.
701 return false;
702
704 return true;
705}
706
708{
709 // bail out if we are not changing the state
712 return;
713
714 // change the state
715 if (enable) {
717 } else {
719 if (!(state.loadRelaxed() & suspendingOrSuspended))
721 }
722}
723
725{
727 return;
728
729 for (int i = 0; i < outputConnections.size(); ++i)
730 outputConnections.at(i)->postCallOutEvent(callOutEvent);
731}
732
734 const QFutureCallOutEvent &callOutEvent2)
735{
737 return;
738
739 for (int i = 0; i < outputConnections.size(); ++i) {
740 QFutureCallOutInterface *interface = outputConnections.at(i);
741 interface->postCallOutEvent(callOutEvent1);
742 interface->postCallOutEvent(callOutEvent2);
743 }
744}
745
746// This function connects an output interface (for example a QFutureWatcher)
747// to this future. While holding the lock we check the state and ready results
748// and add the appropriate callouts to the queue. In order to avoid deadlocks,
749// the actual callouts are made at the end while not holding the lock.
751{
752 QMutexLocker locker(&m_mutex);
753
755
756 const auto currentState = state.loadRelaxed();
757 if (currentState & QFutureInterfaceBase::Started) {
759 if (m_progress) {
761 m_progress->minimum,
762 m_progress->maximum));
765 m_progress->text));
766 } else {
768 0,
769 0));
772 QString()));
773 }
774 }
775
776 if (!hasException) {
778 while (it != data.m_results.end()) {
779 const int begin = it.resultIndex();
780 const int end = begin + it.batchSize();
782 begin,
783 end));
784 it.batchedAdvance();
785 }
786 }
787
788 if (currentState & QFutureInterfaceBase::Suspended)
790 else if (currentState & QFutureInterfaceBase::Suspending)
792
793 if (currentState & QFutureInterfaceBase::Canceled)
795
796 if (currentState & QFutureInterfaceBase::Finished)
798
800
801 locker.unlock();
802 for (auto &&event : events)
803 interface->postCallOutEvent(*event);
804}
805
807{
810 if (index == -1)
811 return;
813
814 interface->callOutInterfaceDisconnected();
815}
816
818{
820}
821
823{
824 setContinuation(std::move(func), nullptr);
825}
826
828 QFutureInterfaceBasePrivate *continuationFutureData)
829{
830 QMutexLocker lock(&d->continuationMutex);
831
832 // If the state is ready, run continuation immediately,
833 // otherwise save it for later.
834 if (isFinished()) {
835 lock.unlock();
836 func(*this);
837 lock.relock();
838 }
839 // Unless the continuation has been cleaned earlier, we have to
840 // store the move-only continuation, to guarantee that the associated
841 // future's data stays alive.
842 if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned) {
843 if (d->continuation) {
844 qWarning() << "Adding a continuation to a future which already has a continuation. "
845 "The existing continuation is overwritten.";
846 }
847 d->continuation = std::move(func);
848 d->continuationData = continuationFutureData;
849 }
850}
851
853{
854 if (!d)
855 return;
856
857 QMutexLocker lock(&d->continuationMutex);
858 d->continuation = nullptr;
859 d->continuationState = QFutureInterfaceBasePrivate::Cleaned;
860 d->continuationData = nullptr;
861}
862
864{
865 QMutexLocker lock(&d->continuationMutex);
866 if (d->continuation) {
867 // Save the continuation in a local function, to avoid calling
868 // a null std::function below, in case cleanContinuation() is
869 // called from some other thread right after unlock() below.
870 auto fn = std::move(d->continuation);
871 lock.unlock();
872 fn(*this);
873
874 lock.relock();
875 // Unless the continuation has been cleaned earlier, we have to
876 // store the move-only continuation, to guarantee that the associated
877 // future's data stays alive.
878 if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned)
879 d->continuation = std::move(fn);
880 }
881}
882
884{
885 return isCanceled() || d->continuationState == QFutureInterfaceBasePrivate::Canceled;
886}
887
889{
890 d->launchAsync = value;
891}
892
894{
895 return d->launchAsync;
896}
897
898namespace QtFuture {
899
901{
903 promise.reportStarted();
904 promise.reportFinished();
905
906 return promise.future();
907}
908
909} // namespace QtFuture
910
\inmodule QtCore
Definition qatomic.h:112
void storeRelaxed(T newValue) noexcept
T loadRelaxed() const noexcept
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
void start() noexcept
Starts this timer.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
\inmodule QtCore
Definition qexception.h:22
virtual void raise() const
In your QException subclass, reimplement raise() like this:
virtual ~QFutureCallOutInterface()
virtual void postCallOutEvent(const QFutureCallOutEvent &)=0
bool internal_updateProgress(int progress, const QString &progressText=QString())
void sendCallOuts(const QFutureCallOutEvent &callOut1, const QFutureCallOutEvent &callOut2)
void sendCallOut(const QFutureCallOutEvent &callOut)
void setState(QFutureInterfaceBase::State state)
QList< QFutureCallOutInterface * > outputConnections
void disconnectOutputInterface(QFutureCallOutInterface *iface)
bool internal_updateProgressValue(int progress)
bool internal_isResultReadyAt(int index) const
void connectOutputInterface(QFutureCallOutInterface *iface)
void internal_setThrottled(bool enable)
QFutureInterfaceBasePrivate * continuationData
QScopedPointer< ProgressData > m_progress
QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState)
QtPrivate::ExceptionStore & exceptionStore()
QThreadPool * threadPool() const
QtPrivate::ResultStoreBase & resultStoreBase()
void waitForResult(int resultIndex)
bool queryState(State state) const
bool isProgressUpdateNeeded() const
void setThreadPool(QThreadPool *pool)
void setExpectedResultCount(int resultCount)
QFutureInterfaceBase(State initialState=NoState)
void reportResultsReady(int beginIndex, int endIndex)
void setContinuation(std::function< void(const QFutureInterfaceBase &)> func)
void setLaunchAsync(bool value)
bool derefT() const noexcept
void setThrottled(bool enable)
void setSuspended(bool suspend)
void setProgressValue(int progressValue)
void setProgressRange(int minimum, int maximum)
QFutureInterfaceBase & operator=(const QFutureInterfaceBase &other)
void swap(QFutureInterfaceBase &other) noexcept
bool refT() const noexcept
void reportException(const QException &e)
QString progressText() const
void setProgressValueAndText(int progressValue, const QString &progressText)
void setRunnable(QRunnable *runnable)
void setFilterMode(bool enable)
bool isResultReadyAt(int index) const
QFuture< T > future()
Definition qfuture.h:320
bool reportFinished(const T *result)
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
void removeAt(qsizetype i)
Definition qlist.h:573
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmutex.h:317
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:323
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
Definition qrunnable.h:18
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
\inmodule QtCore
Definition qthreadpool.h:20
void releaseThread()
Releases a thread previously reserved by a call to reserveThread().
T & emplace_back(Args &&...args)
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
Q_NORETURN void rethrowException() const
void setFilterMode(bool enable)
qSwap(pi, e)
QSet< QString >::iterator it
else opt state
[0]
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
Q_CORE_EXPORT QFuture< void > makeReadyVoidFuture()
static jboolean copy(JNIEnv *, jobject)
#define QT_WARNING_DISABLE_GCC(text)
#define Q_IMPL_EVENT_COMMON(Class)
Definition qcoreevent.h:31
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
@ MaxProgressEmitsPerSecond
static int switch_from_to(QAtomicInt &a, int from, int to)
static int switch_off(QAtomicInt &a, int which)
static int switch_on(QAtomicInt &a, int which)
#define qWarning
Definition qlogging.h:162
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum mode
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLuint GLuint end
GLboolean enable
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
struct _cl_event * event
GLenum func
Definition qopenglext.h:663
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
static void qYieldCpu()
Definition qsimd_p.h:403
ptrdiff_t qsizetype
Definition qtypes.h:70
future suspend()
QReadWriteLock lock
[0]
const QSemaphoreReleaser releaser(sem)
[4]
QSharedPointer< T > other(t)
[5]
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:955
QtPrivate::ExceptionStore m_exceptionStore
QtPrivate::ResultStoreBase m_results