QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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#endif
30#include <QSemaphore>
31#include <QCoreApplication>
32#include <memory>
33
34#ifdef __clang_analyzer__
35#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS do {} while(false);
36#elif defined(AGGRESSIVE_SAFE_MODE)
37#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() ); }
38#elif defined(QGISDEBUG)
39#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(); }
40#else
41#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS do {} while(false);
42#endif
43
44// !!DO NOT USE THIS FOR NEW CODE !!
45// This is in place to keep legacy code running and should be removed in the future.
46#ifdef __clang_analyzer__
47#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL do {} while(false);
48#elif defined(QGISDEBUG)
49#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL 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(); }
50#else
51#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL do {} while(false);
52#endif
53
54#ifdef __clang_analyzer__
55#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) do {} while(false);(void)other;
56#elif defined(AGGRESSIVE_SAFE_MODE)
57#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() ); }
58#elif defined(QGISDEBUG)
59#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(); }
60#else
61#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other) do {} while(false);(void)other;
62#endif
63
64
71class CORE_EXPORT QgsThreadingUtils
72{
73 public:
74
91 template <typename Func>
92 static bool runOnMainThread( const Func &func, QgsFeedback *feedback = nullptr )
93 {
94 // Make sure we only deal with the vector layer on the main thread where it lives.
95 // Anything else risks a crash.
96 if ( QThread::currentThread() == qApp->thread() )
97 {
98 func();
99 return true;
100 }
101 else
102 {
103 if ( feedback )
104 {
105 // This semaphore will block the worker thread until the main thread is ready.
106 // Ready means the event to execute the waitFunc has arrived in the event loop
107 // and is being executed.
108 QSemaphore semaphoreMainThreadReady( 1 );
109
110 // This semaphore will block the main thread until the worker thread is ready.
111 // Once the main thread is executing the waitFunc, it will wait for this semaphore
112 // to be released. This way we can make sure that
113 QSemaphore semaphoreWorkerThreadReady( 1 );
114
115 // Acquire both semaphores. We want the main thread and the current thread to be blocked
116 // until it's safe to continue.
117 semaphoreMainThreadReady.acquire();
118 semaphoreWorkerThreadReady.acquire();
119
120 const std::function<void()> waitFunc = [&semaphoreMainThreadReady, &semaphoreWorkerThreadReady]()
121 {
122 // This function is executed on the main thread. As soon as it's executed
123 // it will tell the worker thread that the main thread is blocked by releasing
124 // the semaphore.
125 semaphoreMainThreadReady.release();
126
127 // ... and wait for the worker thread to release its semaphore
128 semaphoreWorkerThreadReady.acquire();
129 };
130
131 QMetaObject::invokeMethod( qApp, waitFunc, Qt::QueuedConnection );
132
133 // while we are in the event queue for the main thread and not yet
134 // being executed, check all 100 ms if the feedback is canceled.
135 while ( !semaphoreMainThreadReady.tryAcquire( 1, 100 ) )
136 {
137 if ( feedback->isCanceled() )
138 {
139 semaphoreWorkerThreadReady.release();
140 return false;
141 }
142 }
143
144 // finally, the main thread is blocked and we are (most likely) not canceled.
145 // let's do the real work!!
146 func();
147
148 // work done -> tell the main thread he may continue
149 semaphoreWorkerThreadReady.release();
150 return true;
151 }
152 QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
153 return true;
154 }
155 }
156
157};
158
159
160#endif
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
Provides threading utilities for QGIS.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.