From 80750d5fa449c25e53a946d7b04c82d66cd6f7e0 Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Wed, 3 Nov 2021 11:59:28 +0100 Subject: [PATCH] Fix QgsBlockingNetworkRequest race condition issue --- .ci/test_blocklist_qt5.txt | 4 ---- src/core/network/qgsnetworkaccessmanager.cpp | 19 +++++-------------- src/core/network/qgsnetworkaccessmanager.h | 8 +++----- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/.ci/test_blocklist_qt5.txt b/.ci/test_blocklist_qt5.txt index 9face3a2c5e..9c7ff412843 100644 --- a/.ci/test_blocklist_qt5.txt +++ b/.ci/test_blocklist_qt5.txt @@ -22,7 +22,3 @@ test_core_layerdefinition # MSSQL requires the MSSQL docker PyQgsProviderConnectionMssql - -# Too fragile at the moment -test_core_networkaccessmanager - diff --git a/src/core/network/qgsnetworkaccessmanager.cpp b/src/core/network/qgsnetworkaccessmanager.cpp index b6bdbebe13a..23ced757f4a 100644 --- a/src/core/network/qgsnetworkaccessmanager.cpp +++ b/src/core/network/qgsnetworkaccessmanager.cpp @@ -217,6 +217,7 @@ QgsNetworkAccessManager *QgsNetworkAccessManager::instance( Qt::ConnectionType c QgsNetworkAccessManager::QgsNetworkAccessManager( QObject *parent ) : QNetworkAccessManager( parent ) + , mAuthRequestHandlerSemaphore( 1 ) { setProxyFactory( new QgsNetworkProxyFactory() ); setCookieJar( new QgsNetworkCookieJar( this ) ); @@ -466,12 +467,6 @@ void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply ) } } -void QgsNetworkAccessManager::unlockAfterAuthRequestHandled() -{ - Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() ); - mAuthRequestWaitCondition.wakeOne(); -} - void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply ) { if ( reply->manager() == this ) @@ -479,11 +474,6 @@ void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply ) restartTimeout( reply ); emit authRequestHandled( reply ); } - else if ( this == sMainNAM ) - { - // notify other threads to allow them to handle the reply - qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterAuthRequestHandled(); // safe to call directly - the other thread will be stuck waiting for us - } } void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply ) @@ -534,6 +524,7 @@ void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticat emit requestRequiresAuth( getRequestId( reply ), auth->realm() ); + mAuthRequestHandlerSemaphore.acquire(); // in main thread this will trigger auth handler immediately and return once the request is satisfied, // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block emit authRequestOccurred( reply, auth ); @@ -542,9 +533,8 @@ void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticat { // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume // without actually giving the main thread the chance to act on the ssl error and possibly ignore it. - mAuthRequestHandlerMutex.lock(); - mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex ); - mAuthRequestHandlerMutex.unlock(); + mAuthRequestHandlerSemaphore.acquire(); + mAuthRequestHandlerSemaphore.release(); afterAuthRequestHandled( reply ); } } @@ -587,6 +577,7 @@ void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenti emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() ); afterAuthRequestHandled( reply ); + qobject_cast( reply->manager() )->mAuthRequestHandlerSemaphore.release(); } QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control ) diff --git a/src/core/network/qgsnetworkaccessmanager.h b/src/core/network/qgsnetworkaccessmanager.h index 4a5acd4685d..8a92eff4bc6 100644 --- a/src/core/network/qgsnetworkaccessmanager.h +++ b/src/core/network/qgsnetworkaccessmanager.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "qgis_core.h" @@ -800,7 +801,6 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager void afterSslErrorHandled( QNetworkReply *reply ); #endif - void unlockAfterAuthRequestHandled(); void afterAuthRequestHandled( QNetworkReply *reply ); void pauseTimeout( QNetworkReply *reply ); @@ -824,10 +824,8 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager // auth request handler, will be set for main thread ONLY std::unique_ptr< QgsNetworkAuthenticationHandler > mAuthHandler; - // only in use by worker threads, unused in main thread - QMutex mAuthRequestHandlerMutex; - // only in use by worker threads, unused in main thread - QWaitCondition mAuthRequestWaitCondition; + // Used by worker threads to wait for authentification handler run in main thread + QSemaphore mAuthRequestHandlerSemaphore; }; #endif // QGSNETWORKACCESSMANAGER_H