QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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
6  email : [email protected]
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 
23 #include "qgsfeedback.h"
24 
25 #include <QThread>
26 #include <QSemaphore>
27 #include <memory>
28 
35 class CORE_EXPORT QgsThreadingUtils
36 {
37  public:
38 
55  template <typename Func>
56  static bool runOnMainThread( const Func &func, QgsFeedback *feedback = nullptr )
57  {
58 #if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
59  // Make sure we only deal with the vector layer on the main thread where it lives.
60  // Anything else risks a crash.
61  if ( QThread::currentThread() == qApp->thread() )
62  {
63  func();
64  return true;
65  }
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  }
117  QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
118  return true;
119  }
120 #else
121  Q_UNUSED( feedback )
122  func();
123  return true;
124 #endif
125  }
126 
127 };
128 
129 
130 #endif
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Provides threading utilities for QGIS.