From: Olivier Goffart Date: Thu, 7 Jan 2016 13:14:32 +0000 (+0100) Subject: HTTP creds: Fix GUI blocking for 5 seconds when entering wrong password X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~1254^2~96 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=868d4c781a8c74386421c89801cf15805d56e245;p=nextcloud-desktop.git HTTP creds: Fix GUI blocking for 5 seconds when entering wrong password The Qt HTTP thread calls authenticationRequired (indirectly) using a BlockingQueuedConnection. So when we call invalidateToken from slot connected to this signal and end up calling QNAM::clearAccessCache which waits on the thread for 5 seconds Backtraces: Qt HTTP thread: #0 0x00007ffff20c707f in pthread_cond_wait@@GLIBC_2.3.2 () #1 0x00007ffff43f0c0b in QWaitConditionPrivate::wait #2 QWaitCondition::wait #3 0x00007ffff43ea06b in QSemaphore::acquire #4 0x00007ffff45dcf6f in QMetaObject::activate [...] #9 0x00007ffff45dd607 in QMetaObject::activate #10 0x00007ffff4edbaf7 in QHttpNetworkReply::authenticationRequired #11 0x00007ffff4e0b2b4 in QHttpNetworkConnectionPrivate::handleAuthenticateChallenge #12 0x00007ffff4e10753 in QHttpNetworkConnectionChannel::handleStatus #13 0x00007ffff4e11cc9 in QHttpNetworkConnectionChannel::allDone #14 0x00007ffff4e14605 in QHttpProtocolHandler::_q_receiveReply Main Thread: #0 0x00007ffff20c7428 in pthread_cond_timedwait@@GLIBC_2.3.2 () #1 0x00007ffff43f0b56 in QWaitConditionPrivate::wait_relative (time=5000, this=0x136c580) #2 QWaitConditionPrivate::wait (time=5000, this=0x136c580) #3 QWaitCondition::wait (this=this@entry=0x136c788, mutex=mutex@entry=0x136c760, time=time@entry=5000) #4 0x00007ffff43efa6e in QThread::wait (this=, time=time@entry=5000) #5 0x00007ffff4e1edd3 in QNetworkAccessManagerPrivate::clearCache #6 0x00007ffff7b6fb03 in OCC::HttpCredentials::invalidateToken() #7 0x000000000057adb4 in OCC::AccountState::slotInvalidCredentials() #8 0x000000000057ac76 in OCC::AccountState::slotConnectionValidatorResult(OCC::ConnectionValidator::Status, QStringList const&) () #9 0x00000000005ab45c in OCC::AccountState::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) #10 0x00007ffff45dcd30 in QMetaObject::activate #11 0x00007ffff7b78671 in OCC::ConnectionValidator::connectionResult(OCC::ConnectionValidator::Status, QStringList) () #12 0x00007ffff7ae2514 in OCC::ConnectionValidator::reportResult(OCC::ConnectionValidator::Status) () #13 0x00007ffff7ae39b7 in OCC::ConnectionValidator::slotAuthFailed(QNetworkReply*) () #14 0x00007ffff7b784a9 in OCC::ConnectionValidator::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () #15 0x00007ffff45dcd30 in QMetaObject::activate #16 0x00007ffff7b766dc in OCC::AbstractNetworkJob::networkError(QNetworkReply*) #17 0x00007ffff7af9f6e in OCC::AbstractNetworkJob::slotFinished() #18 0x00007ffff7b7654d in OCC::AbstractNetworkJob::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () #20 0x00007ffff45dd607 in QMetaObject::activate #21 0x00007ffff4edd143 in QNetworkReply::finished #22 0x00007ffff4e3fec7 in QNetworkReplyHttpImplPrivate::finished #23 0x00007ffff4e41818 in QNetworkReplyHttpImpl::close #24 0x00007ffff7b7047b in OCC::HttpCredentials::slotAuthentication(QNetworkReply*, QAuthenticator*) () #25 0x00007ffff7b79092 in OCC::HttpCredentials::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () #27 0x00007ffff45dd607 in QMetaObject::activate #28 0x00007ffff4e1d6fb in QNetworkAccessManager::authenticationRequired #29 0x00007ffff4e1ea07 in QNetworkAccessManagerPrivate::authenticationRequired #30 0x00007ffff4e3c784 in QNetworkReplyHttpImplPrivate::httpAuthenticationRequired Another case of Main Thread: #5 0x00007ffff4e1edd3 in QNetworkAccessManagerPrivate::clearCache #6 0x00007ffff7b6fb03 in OCC::HttpCredentials::invalidateToken() #7 0x000000000057b1e4 in OCC::AccountState::slotInvalidCredentials() () #8 0x00000000005abb8a in OCC::AccountState::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () #9 0x00007ffff45dcd30 in QMetaObject::activate #10 0x00007ffff7b76ed5 in OCC::Account::invalidCredentials() () #11 0x00007ffff7ad55f5 in OCC::Account::handleInvalidCredentials() #12 0x00007ffff7afa69a in OCC::AbstractNetworkJob::slotFinished() #13 0x00007ffff7b7654d in OCC::AbstractNetworkJob::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () #15 0x00007ffff45dd607 in QMetaObject::activate #16 0x00007ffff4edd143 in QNetworkReply::finished #17 0x00007ffff4e3fec7 in QNetworkReplyHttpImplPrivate::finished #18 0x00007ffff4e41818 in QNetworkReplyHttpImpl::close #19 0x00007ffff7b7047b in OCC::HttpCredentials::slotAuthentication(QNetworkReply*, QAuthenticator*) () #20 0x00007ffff7b79092 in OCC::HttpCredentials::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () #22 0x00007ffff45dd607 in QMetaObject::activate #23 0x00007ffff4e1d6fb in QNetworkAccessManager::authenticationRequired --- diff --git a/src/libsync/creds/httpcredentials.cpp b/src/libsync/creds/httpcredentials.cpp index c60f85422..d30c009f0 100644 --- a/src/libsync/creds/httpcredentials.cpp +++ b/src/libsync/creds/httpcredentials.cpp @@ -249,6 +249,15 @@ void HttpCredentials::invalidateToken() _account->clearCookieJar(); // let QNAM forget about the password + // This needs to be done later in the event loop because we might be called (directly or + // indirectly) from QNetworkAccessManagerPrivate::authenticationRequired, which itself + // is a called from a BlockingQueuedConnection from the Qt HTTP thread. And clearing the + // cache needs to synchronize again with the HTTP thread. + QTimer::singleShot(0, this, SLOT(clearQNAMCache())); +} + +void HttpCredentials::clearQNAMCache() +{ #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) _account->networkAccessManager()->clearAccessCache(); #else diff --git a/src/libsync/creds/httpcredentials.h b/src/libsync/creds/httpcredentials.h index 96467ff89..1032354c7 100644 --- a/src/libsync/creds/httpcredentials.h +++ b/src/libsync/creds/httpcredentials.h @@ -61,6 +61,7 @@ private Q_SLOTS: void slotAuthentication(QNetworkReply*, QAuthenticator*); void slotReadJobDone(QKeychain::Job*); void slotWriteJobDone(QKeychain::Job*); + void clearQNAMCache(); protected: QString _user;