Skip to content

Commit 2a4684a

Browse files
committedJun 20, 2014
Fix #10655 (race condition in QgsCredentials)
Example of race condition during rendering: Threads 1 and 2 call get(), it checks that there are cached credentials. Thread 1 takes the cached credentials, thread 2 will get no data -> will request credentials in dialog
1 parent 61e934b commit 2a4684a

File tree

5 files changed

+62
-0
lines changed

5 files changed

+62
-0
lines changed
 

‎python/core/qgscredentials.sip

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,30 @@ class QgsCredentials
1414
//! retrieves instance
1515
static QgsCredentials *instance();
1616

17+
/**
18+
* Lock the instance against access from multiple threads. This does not really lock access to get/put methds,
19+
* it will just prevent other threads to lock the instance and continue the execution. When the class is used
20+
* from non-GUI threads, they should call lock() before the get/put calls to avoid race conditions.
21+
* @note added in 2.4
22+
*/
23+
void lock();
24+
/**
25+
* Unlock the instance after being locked.
26+
* @note added in 2.4
27+
*/
28+
void unlock();
29+
1730
protected:
31+
QgsCredentials();
32+
1833
//! request a password
1934
virtual bool request( QString realm, QString &username /In,Out/, QString &password /In,Out/, QString message = QString::null ) = 0;
2035

2136
//! register instance
2237
void setInstance( QgsCredentials *theInstance );
38+
39+
private:
40+
QgsCredentials( const QgsCredentials& );
2341
};
2442

2543

‎src/core/qgscredentials.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ QgsCredentials *QgsCredentials::instance()
3838
return new QgsCredentialsConsole();
3939
}
4040

41+
QgsCredentials::QgsCredentials()
42+
{
43+
}
44+
4145
QgsCredentials::~QgsCredentials()
4246
{
4347
}
@@ -70,6 +74,18 @@ void QgsCredentials::put( QString realm, QString username, QString password )
7074
mCredentialCache.insert( realm, QPair<QString, QString>( username, password ) );
7175
}
7276

77+
78+
void QgsCredentials::lock()
79+
{
80+
mMutex.lock();
81+
}
82+
83+
void QgsCredentials::unlock()
84+
{
85+
mMutex.unlock();
86+
}
87+
88+
7389
////////////////////////////////
7490
// QgsCredentialsConsole
7591

‎src/core/qgscredentials.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <QObject>
2222
#include <QPair>
2323
#include <QMap>
24+
#include <QMutex>
2425

2526
/** \ingroup core
2627
* Interface for requesting credentials in QGIS in GUI independent way.
@@ -45,19 +46,38 @@ class CORE_EXPORT QgsCredentials
4546
//! retrieves instance
4647
static QgsCredentials *instance();
4748

49+
/**
50+
* Lock the instance against access from multiple threads. This does not really lock access to get/put methds,
51+
* it will just prevent other threads to lock the instance and continue the execution. When the class is used
52+
* from non-GUI threads, they should call lock() before the get/put calls to avoid race conditions.
53+
* @note added in 2.4
54+
*/
55+
void lock();
56+
/**
57+
* Unlock the instance after being locked.
58+
* @note added in 2.4
59+
*/
60+
void unlock();
61+
4862
protected:
63+
QgsCredentials();
64+
4965
//! request a password
5066
virtual bool request( QString realm, QString &username, QString &password, QString message = QString::null ) = 0;
5167

5268
//! register instance
5369
void setInstance( QgsCredentials *theInstance );
5470

5571
private:
72+
Q_DISABLE_COPY( QgsCredentials )
73+
5674
//! cache for already requested credentials in this session
5775
QMap< QString, QPair<QString, QString> > mCredentialCache;
5876

5977
//! Pointer to the credential instance
6078
static QgsCredentials *smInstance;
79+
80+
QMutex mMutex;
6181
};
6282

6383

‎src/providers/oracle/qgsoracleconn.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ QgsOracleConn::QgsOracleConn( QgsDataSourceURI uri )
8080
if ( !username.isEmpty() )
8181
realm.prepend( username + "@" );
8282

83+
QgsCredentials::instance()->lock();
84+
8385
while ( !mDatabase.open() )
8486
{
8587
bool ok = QgsCredentials::instance()->get( realm, username, password, mDatabase.lastError().text() );
@@ -102,6 +104,8 @@ QgsOracleConn::QgsOracleConn( QgsDataSourceURI uri )
102104

103105
if ( mDatabase.isOpen() )
104106
QgsCredentials::instance()->put( realm, username, password );
107+
108+
QgsCredentials::instance()->unlock();
105109
}
106110

107111
if ( !mDatabase.isOpen() )

‎src/providers/postgres/qgspostgresconn.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ QgsPostgresConn::QgsPostgresConn( QString conninfo, bool readOnly, bool shared )
181181
QString username = uri.username();
182182
QString password = uri.password();
183183

184+
QgsCredentials::instance()->lock();
185+
184186
while ( PQstatus() != CONNECTION_OK )
185187
{
186188
bool ok = QgsCredentials::instance()->get( conninfo, username, password, PQerrorMessage() );
@@ -201,6 +203,8 @@ QgsPostgresConn::QgsPostgresConn( QString conninfo, bool readOnly, bool shared )
201203

202204
if ( PQstatus() == CONNECTION_OK )
203205
QgsCredentials::instance()->put( conninfo, username, password );
206+
207+
QgsCredentials::instance()->unlock();
204208
}
205209

206210
if ( PQstatus() != CONNECTION_OK )

1 commit comments

Comments
 (1)

3nids commented on Jun 20, 2014

@3nids
Member

thanks, will test this on monday!

Please sign in to comment.