16#ifndef QGSTHREADINGUTILS_H
17#define QGSTHREADINGUTILS_H
27#if defined(QGISDEBUG) || defined(AGGRESSIVE_SAFE_MODE)
32#include <QCoreApplication>
35#ifdef __clang_analyzer__
36#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS do {} while(false);
37#elif defined(AGGRESSIVE_SAFE_MODE)
38#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS if ( QThread::currentThread() != thread() ) {qFatal( "%s", QStringLiteral("%2 (%1:%3) is run from a different thread than the object %4 lives in [0x%5 vs 0x%6]" ).arg( QString( __FILE__ ), QString( __FUNCTION__ ), QString::number( __LINE__ ), objectName() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ).arg( reinterpret_cast< qint64 >( thread() ), 0, 16 ).toLocal8Bit().constData() ); }
39#elif defined(QGISDEBUG)
40#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS if ( QThread::currentThread() != thread() ) {qWarning() << QStringLiteral("%2 (%1:%3) is run from a different thread than the object %4 lives in [0x%5 vs 0x%6]" ).arg( QString( __FILE__ ), QString( __FUNCTION__ ), QString::number( __LINE__ ), objectName() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ).arg( reinterpret_cast< qint64 >( thread() ), 0, 16 ).toLocal8Bit().constData(); }
42#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS do {} while(false);
47#ifdef __clang_analyzer__
48#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL do {} while(false);
49#elif defined(QGISDEBUG)
50#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL if ( QThread::currentThread() != thread() ) { const QString location = QStringLiteral("%1 (%2:%3)").arg( QString( __FUNCTION__ ) ,QString( __FILE__ ), QString::number( __LINE__ ) ); QgsThreadingUtils::sEmittedWarningMutex.lock(); if ( !QgsThreadingUtils::sEmittedWarnings.contains( location ) ) { qWarning() << QStringLiteral("%1 is run from a different thread than the object %2 lives in [0x%3 vs 0x%4]" ).arg( location, objectName() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ).arg( reinterpret_cast< qint64 >( thread() ), 0, 16 ).toLocal8Bit().constData(); QgsThreadingUtils::sEmittedWarnings.insert( location ); } QgsThreadingUtils::sEmittedWarningMutex.unlock(); }
52#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL do {} while(false);
55#ifdef __clang_analyzer__
56#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) do {} while(false);(void)other;
57#elif defined(AGGRESSIVE_SAFE_MODE)
58#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) if ( other->thread() != thread() ) {qFatal( "%s", QStringLiteral("%2 (%1:%3) Object %4 is from a different thread than the object %5 lives in [0x%6 vs 0x%7]" ).arg( QString( __FILE__ ), QString( __FUNCTION__ ), QString::number( __LINE__ ), other->objectName(), objectName() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ).arg( reinterpret_cast< qint64 >( thread() ), 0, 16 ).toLocal8Bit().constData() ); }
59#elif defined(QGISDEBUG)
60#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) if ( other->thread() != thread() ) {qWarning() << QStringLiteral("%2 (%1:%3) Object %4 is from a different thread than the object %5 lives in [0x%6 vs 0x%7]" ).arg( QString( __FILE__ ), QString( __FUNCTION__ ), QString::number( __LINE__ ), other->objectName(), objectName() ).arg( reinterpret_cast< qint64 >( QThread::currentThread() ), 0, 16 ).arg( reinterpret_cast< qint64 >( thread() ), 0, 16 ).toLocal8Bit().constData(); }
62#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) do {} while(false);(void)other;
86 Q_ASSERT_X( mObject->thread() ==
nullptr || mObject->thread() == QThread::currentThread(),
"QgsScopedAssignObjectToCurrentThread",
"QObject was already assigned to a different thread!" );
87 if ( mObject->thread() != QThread::currentThread() )
88 mObject->moveToThread( QThread::currentThread() );
93 mObject->moveToThread(
nullptr );
102 QObject *mObject =
nullptr;
131 template <
typename Func>
136 if ( QThread::currentThread() == qApp->thread() )
148 QSemaphore semaphoreMainThreadReady( 1 );
153 QSemaphore semaphoreWorkerThreadReady( 1 );
157 semaphoreMainThreadReady.acquire();
158 semaphoreWorkerThreadReady.acquire();
160 const std::function<void()> waitFunc = [&semaphoreMainThreadReady, &semaphoreWorkerThreadReady]()
165 semaphoreMainThreadReady.release();
168 semaphoreWorkerThreadReady.acquire();
171 QMetaObject::invokeMethod( qApp, waitFunc, Qt::QueuedConnection );
175 while ( !semaphoreMainThreadReady.tryAcquire( 1, 100 ) )
177 if ( feedback->isCanceled() )
179 semaphoreWorkerThreadReady.release();
189 semaphoreWorkerThreadReady.release();
192 QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
196#if defined(QGISDEBUG)
198 static QSet< QString > sEmittedWarnings;
200 static QMutex sEmittedWarningMutex;
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Temporarily moves a QObject to the current thread, then resets it back to nullptr thread on destructi...
QgsScopedAssignObjectToCurrentThread(QObject *object)
Assigns object to the current thread.
QgsScopedAssignObjectToCurrentThread & operator=(const QgsScopedAssignObjectToCurrentThread &)=delete
QgsScopedAssignObjectToCurrentThread cannot be copied.
~QgsScopedAssignObjectToCurrentThread()
QgsScopedAssignObjectToCurrentThread(const QgsScopedAssignObjectToCurrentThread &other)=delete
QgsScopedAssignObjectToCurrentThread cannot be copied.
Provides threading utilities for QGIS.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.