QGIS API Documentation  3.20.0-Odense (decaadbb31)
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  // Make sure we only deal with the vector layer on the main thread where it lives.
59  // Anything else risks a crash.
60  if ( QThread::currentThread() == qApp->thread() )
61  {
62  func();
63  return true;
64  }
65  else
66  {
67  if ( feedback )
68  {
69  // This semaphore will block the worker thread until the main thread is ready.
70  // Ready means the event to execute the waitFunc has arrived in the event loop
71  // and is being executed.
72  QSemaphore semaphoreMainThreadReady( 1 );
73 
74  // This semaphore will block the main thread until the worker thread is ready.
75  // Once the main thread is executing the waitFunc, it will wait for this semaphore
76  // to be released. This way we can make sure that
77  QSemaphore semaphoreWorkerThreadReady( 1 );
78 
79  // Acquire both semaphores. We want the main thread and the current thread to be blocked
80  // until it's save to continue.
81  semaphoreMainThreadReady.acquire();
82  semaphoreWorkerThreadReady.acquire();
83 
84  std::function<void()> waitFunc = [&semaphoreMainThreadReady, &semaphoreWorkerThreadReady]()
85  {
86  // This function is executed on the main thread. As soon as it's executed
87  // it will tell the worker thread that the main thread is blocked by releasing
88  // the semaphore.
89  semaphoreMainThreadReady.release();
90 
91  // ... and wait for the worker thread to release its semaphore
92  semaphoreWorkerThreadReady.acquire();
93  };
94 
95  QMetaObject::invokeMethod( qApp, waitFunc, Qt::QueuedConnection );
96 
97  // while we are in the event queue for the main thread and not yet
98  // being executed, check all 100 ms if the feedback is canceled.
99  while ( !semaphoreMainThreadReady.tryAcquire( 1, 100 ) )
100  {
101  if ( feedback->isCanceled() )
102  {
103  semaphoreWorkerThreadReady.release();
104  return false;
105  }
106  }
107 
108  // finally, the main thread is blocked and we are (most likely) not canceled.
109  // let's do the real work!!
110  func();
111 
112  // work done -> tell the main thread he may continue
113  semaphoreWorkerThreadReady.release();
114  return true;
115  }
116  QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
117  return true;
118  }
119  }
120 
121 };
122 
123 
124 #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.