QGIS API Documentation 3.39.0-Master (3aed037ce22)
Loading...
Searching...
No Matches
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
98
99 private:
100 QObject *mObject = nullptr;
101};
102
109class CORE_EXPORT QgsThreadingUtils
110{
111 public:
112
129 template <typename Func>
130 static bool runOnMainThread( const Func &func, QgsFeedback *feedback = nullptr )
131 {
132 // Make sure we only deal with the vector layer on the main thread where it lives.
133 // Anything else risks a crash.
134 if ( QThread::currentThread() == qApp->thread() )
135 {
136 func();
137 return true;
138 }
139 else
140 {
141 if ( feedback )
142 {
143 // This semaphore will block the worker thread until the main thread is ready.
144 // Ready means the event to execute the waitFunc has arrived in the event loop
145 // and is being executed.
146 QSemaphore semaphoreMainThreadReady( 1 );
147
148 // This semaphore will block the main thread until the worker thread is ready.
149 // Once the main thread is executing the waitFunc, it will wait for this semaphore
150 // to be released. This way we can make sure that
151 QSemaphore semaphoreWorkerThreadReady( 1 );
152
153 // Acquire both semaphores. We want the main thread and the current thread to be blocked
154 // until it's safe to continue.
155 semaphoreMainThreadReady.acquire();
156 semaphoreWorkerThreadReady.acquire();
157
158 const std::function<void()> waitFunc = [&semaphoreMainThreadReady, &semaphoreWorkerThreadReady]()
159 {
160 // This function is executed on the main thread. As soon as it's executed
161 // it will tell the worker thread that the main thread is blocked by releasing
162 // the semaphore.
163 semaphoreMainThreadReady.release();
164
165 // ... and wait for the worker thread to release its semaphore
166 semaphoreWorkerThreadReady.acquire();
167 };
168
169 QMetaObject::invokeMethod( qApp, waitFunc, Qt::QueuedConnection );
170
171 // while we are in the event queue for the main thread and not yet
172 // being executed, check all 100 ms if the feedback is canceled.
173 while ( !semaphoreMainThreadReady.tryAcquire( 1, 100 ) )
174 {
175 if ( feedback->isCanceled() )
176 {
177 semaphoreWorkerThreadReady.release();
178 return false;
179 }
180 }
181
182 // finally, the main thread is blocked and we are (most likely) not canceled.
183 // let's do the real work!!
184 func();
185
186 // work done -> tell the main thread he may continue
187 semaphoreWorkerThreadReady.release();
188 return true;
189 }
190 QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
191 return true;
192 }
193 }
194#if defined(QGISDEBUG)
196 static QSet< QString > sEmittedWarnings;
198 static QMutex sEmittedWarningMutex;
199#endif
200
201};
202
203
204#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(const QgsScopedAssignObjectToCurrentThread &other)=delete
Provides threading utilities for QGIS.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.