Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsthreadingutils.h 3 : : -------------------------------------- 4 : : Date : 11.9.2018 5 : : Copyright : (C) 2018 by Matthias Kuhn 6 : : email : matthias@opengis.ch 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 : : 29 : : /** 30 : : * \ingroup core 31 : : * \brief Provides threading utilities for QGIS. 32 : : * 33 : : * \since QGIS 3.4 34 : : */ 35 : : class CORE_EXPORT QgsThreadingUtils 36 : : { 37 : : public: 38 : : 39 : : /** 40 : : * Guarantees that \a func is executed on the main thread. If this is called 41 : : * from another thread, the other thread will be blocked until the function 42 : : * has been executed. 43 : : * This is useful to quickly access information from objects that live on the 44 : : * main thread and copying this information into worker threads. Avoid running 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. 49 : : * 50 : : * \note Only works with Qt >= 5.10, earlier versions will execute the code 51 : : * in the worker thread. 52 : : * 53 : : * \since QGIS 3.4 54 : : */ 55 : : template <typename Func> 56 : 25 : 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 : 25 : if ( QThread::currentThread() == qApp->thread() ) 61 : : { 62 : 25 : func(); 63 : 25 : return true; 64 : : } 65 : : else 66 : : { 67 : 0 : 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 : 0 : 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 : 0 : 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 : 0 : semaphoreMainThreadReady.acquire(); 82 : 0 : semaphoreWorkerThreadReady.acquire(); 83 : : 84 : 0 : 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 : 0 : semaphoreMainThreadReady.release(); 90 : : 91 : : // ... and wait for the worker thread to release its semaphore 92 : 0 : semaphoreWorkerThreadReady.acquire(); 93 : 0 : }; 94 : : 95 : 0 : 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 : 0 : while ( !semaphoreMainThreadReady.tryAcquire( 1, 100 ) ) 100 : : { 101 : 0 : if ( feedback->isCanceled() ) 102 : : { 103 : 0 : semaphoreWorkerThreadReady.release(); 104 : 0 : 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 : 0 : func(); 111 : : 112 : : // work done -> tell the main thread he may continue 113 : 0 : semaphoreWorkerThreadReady.release(); 114 : 0 : return true; 115 : 0 : } 116 : 0 : QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection ); 117 : 0 : return true; 118 : : } 119 : 25 : } 120 : : 121 : : }; 122 : : 123 : : 124 : : #endif