QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsthreadingutils.h
Go to the documentation of this file.
1/***************************************************************************
2 qgsthreadingutils.h
3 --------------------------------------
4 Date : 11.9.2018
5 Copyright : (C) 2018 by Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#ifndef QGSTHREADINGUTILS_H
17#define QGSTHREADINGUTILS_H
18
19#define SIP_NO_FILE
20
21#include "qgis_core.h"
22#include "qgsconfig.h"
23
24#include "qgsfeedback.h"
25
26#include <QThread>
27#if defined(QGISDEBUG) || defined(AGGRESSIVE_SAFE_MODE)
28#include <QDebug>
29#include <QMutex>
30#endif
31#include <QSemaphore>
32#include <QCoreApplication>
33#include <memory>
34
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(); }
41#else
42#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS do {} while(false);
43#endif
44
45// !!DO NOT USE THIS FOR NEW CODE !!
46// This is in place to keep legacy code running and should be removed in the future.
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(); }
51#else
52#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL do {} while(false);
53#endif
54
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(); }
61#else
62#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) do {} while(false);(void)other;
63#endif
64
65
73{
74 public:
75
84 : mObject( object )
85 {
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() );
89 }
90
92 {
93 mObject->moveToThread( nullptr );
94 }
95
100
101 private:
102 QObject *mObject = nullptr;
103};
104
111class CORE_EXPORT QgsThreadingUtils
112{
113 public:
114
131 template <typename Func>
132 static bool runOnMainThread( const Func &func, QgsFeedback *feedback = nullptr )
133 {
134 // Make sure we only deal with the vector layer on the main thread where it lives.
135 // Anything else risks a crash.
136 if ( QThread::currentThread() == qApp->thread() )
137 {
138 func();
139 return true;
140 }
141 else
142 {
143 if ( feedback )
144 {
145 // This semaphore will block the worker thread until the main thread is ready.
146 // Ready means the event to execute the waitFunc has arrived in the event loop
147 // and is being executed.
148 QSemaphore semaphoreMainThreadReady( 1 );
149
150 // This semaphore will block the main thread until the worker thread is ready.
151 // Once the main thread is executing the waitFunc, it will wait for this semaphore
152 // to be released. This way we can make sure that
153 QSemaphore semaphoreWorkerThreadReady( 1 );
154
155 // Acquire both semaphores. We want the main thread and the current thread to be blocked
156 // until it's safe to continue.
157 semaphoreMainThreadReady.acquire();
158 semaphoreWorkerThreadReady.acquire();
159
160 const std::function<void()> waitFunc = [&semaphoreMainThreadReady, &semaphoreWorkerThreadReady]()
161 {
162 // This function is executed on the main thread. As soon as it's executed
163 // it will tell the worker thread that the main thread is blocked by releasing
164 // the semaphore.
165 semaphoreMainThreadReady.release();
166
167 // ... and wait for the worker thread to release its semaphore
168 semaphoreWorkerThreadReady.acquire();
169 };
170
171 QMetaObject::invokeMethod( qApp, waitFunc, Qt::QueuedConnection );
172
173 // while we are in the event queue for the main thread and not yet
174 // being executed, check all 100 ms if the feedback is canceled.
175 while ( !semaphoreMainThreadReady.tryAcquire( 1, 100 ) )
176 {
177 if ( feedback->isCanceled() )
178 {
179 semaphoreWorkerThreadReady.release();
180 return false;
181 }
182 }
183
184 // finally, the main thread is blocked and we are (most likely) not canceled.
185 // let's do the real work!!
186 func();
187
188 // work done -> tell the main thread he may continue
189 semaphoreWorkerThreadReady.release();
190 return true;
191 }
192 QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
193 return true;
194 }
195 }
196#if defined(QGISDEBUG)
198 static QSet< QString > sEmittedWarnings;
200 static QMutex sEmittedWarningMutex;
201#endif
202
203};
204
205
206#endif
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
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(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.