20
20
21
21
#include " qgis_core.h"
22
22
23
+ #include " qgsfeedback.h"
24
+
23
25
#include < QThread>
26
+ #include < QSemaphore>
27
+ #include < memory>
24
28
25
29
/* *
26
30
* \ingroup core
@@ -39,24 +43,84 @@ class CORE_EXPORT QgsThreadingUtils
39
43
* This is useful to quickly access information from objects that live on the
40
44
* main thread and copying this information into worker threads. Avoid running
41
45
* expensive code inside \a func.
46
+ * If a \a feedback is provided, it will observe if the feedback is canceled.
47
+ * In case the feedback is canceled before the main thread started to run the
48
+ * function, it will return without executing the function.
42
49
*
43
50
* \note Only works with Qt >= 5.10, earlier versions will execute the code
44
51
* in the worker thread.
45
52
*
46
53
* \since QGIS 3.4
47
54
*/
48
55
template <typename Func>
49
- static void runOnMainThread ( const Func &func )
56
+ static bool runOnMainThread ( const Func &func, QgsFeedback *feedback = nullptr )
50
57
{
51
58
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
52
59
// Make sure we only deal with the vector layer on the main thread where it lives.
53
60
// Anything else risks a crash.
54
61
if ( QThread::currentThread () == qApp->thread () )
62
+ {
55
63
func ();
64
+ return true ;
65
+ }
56
66
else
67
+ {
68
+ if ( feedback )
69
+ {
70
+ // This semaphore will block the worker thread until the main thread is ready.
71
+ // Ready means the event to execute the waitFunc has arrived in the event loop
72
+ // and is being executed.
73
+ QSemaphore semaphoreMainThreadReady ( 1 );
74
+
75
+ // This semaphore will block the main thread until the worker thread is ready.
76
+ // Once the main thread is executing the waitFunc, it will wait for this semaphore
77
+ // to be released. This way we can make sure that
78
+ QSemaphore semaphoreWorkerThreadReady ( 1 );
79
+
80
+ // Acquire both semaphores. We want the main thread and the current thread to be blocked
81
+ // until it's save to continue.
82
+ semaphoreMainThreadReady.acquire ();
83
+ semaphoreWorkerThreadReady.acquire ();
84
+
85
+ std::function<void ()> waitFunc = [&semaphoreMainThreadReady, &semaphoreWorkerThreadReady]()
86
+ {
87
+ // This function is executed on the main thread. As soon as it's executed
88
+ // it will tell the worker thread that the main thread is blocked by releasing
89
+ // the semaphore.
90
+ semaphoreMainThreadReady.release ();
91
+
92
+ // ... and wait for the worker thread to release its semaphore
93
+ semaphoreWorkerThreadReady.acquire ();
94
+ };
95
+
96
+ QMetaObject::invokeMethod ( qApp, waitFunc, Qt::QueuedConnection );
97
+
98
+ // while we are in the event queue for the main thread and not yet
99
+ // being executed, check all 100 ms if the feedback is canceled.
100
+ while ( !semaphoreMainThreadReady.tryAcquire ( 1 , 100 ) )
101
+ {
102
+ if ( feedback->isCanceled () )
103
+ {
104
+ semaphoreWorkerThreadReady.release ();
105
+ return false ;
106
+ }
107
+ }
108
+
109
+ // finally, the main thread is blocked and we are (most likely) not canceled.
110
+ // let's do the real work!!
111
+ func ();
112
+
113
+ // work done -> tell the main thread he may continue
114
+ semaphoreWorkerThreadReady.release ();
115
+ return true ;
116
+ }
57
117
QMetaObject::invokeMethod ( qApp, func, Qt::BlockingQueuedConnection );
118
+ return true ;
119
+ }
58
120
#else
121
+ Q_UNUSED ( feedback )
59
122
func ();
123
+ return true ;
60
124
#endif
61
125
}
62
126
0 commit comments