Skip to content

Commit dd2ed9c

Browse files
dwadlernyalldawson
authored andcommittedNov 20, 2018
Update src/providers/db2/qgsdb2provider.cpp
1 parent 63aa1e1 commit dd2ed9c

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed
 

‎src/providers/db2/qgsdb2provider.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ static const QString PROVIDER_KEY = QStringLiteral( "DB2" );
3434
static const QString PROVIDER_DESCRIPTION = QStringLiteral( "DB2 Spatial Extender provider" );
3535

3636
int QgsDb2Provider::sConnectionId = 0;
37+
QMutex QgsDb2Provider::sMutex{ QMutex::Recursive };
3738

3839
QgsDb2Provider::QgsDb2Provider( const QString &uri, const ProviderOptions &options )
3940
: QgsVectorDataProvider( uri, options )
@@ -193,17 +194,42 @@ QSqlDatabase QgsDb2Provider::getDatabase( const QString &connInfo, QString &errM
193194
const QString threadSafeConnectionName = dbConnectionName( connectionName );
194195
QgsDebugMsg( "threadSafeConnectionName: " + threadSafeConnectionName );
195196

197+
// while everything we use from QSqlDatabase here is thread safe, we need to ensure
198+
// that the connection cleanup on thread finalization happens in a predictable order
199+
QMutexLocker locker( &sMutex );
200+
196201
/* if new database connection */
197202
if ( !QSqlDatabase::contains( threadSafeConnectionName ) )
198203
{
199204
QgsDebugMsg( QStringLiteral( "new connection. create new QODBC mapping" ) );
200205
db = QSqlDatabase::addDatabase( QStringLiteral( "QODBC3" ), threadSafeConnectionName );
201-
}
206+
db.setConnectOptions( QStringLiteral( "SQL_ATTR_CONNECTION_POOLING=SQL_CP_ONE_PER_HENV" ) );
207+
208+
// for background threads, remove database when current thread finishes
209+
if ( QThread::currentThread() != QCoreApplication::instance()->thread() )
210+
{
211+
QgsDebugMsgLevel( QStringLiteral( "Scheduled auth db remove on thread close" ), 2 );
212+
213+
// IMPORTANT - we use a direct connection here, because the database removal must happen immediately
214+
// when the thread finishes, and we cannot let this get queued on the main thread's event loop.
215+
// Otherwise, the QSqlDatabase's private data's thread gets reset immediately the QThread::finished,
216+
// and a subsequent call to QSqlDatabase::database with the same thread address (yep it happens, actually a lot)
217+
// triggers a condition in QSqlDatabase which detects the nullptr private thread data and returns an invalid database instead.
218+
// QSqlDatabase::removeDatabase is thread safe, so this is ok to do.
219+
QObject::connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName]
220+
{
221+
QMutexLocker locker( &sMutex );
222+
QSqlDatabase::removeDatabase( connectionName );
223+
}, Qt::DirectConnection );
224+
}
225+
}
202226
else /* if existing database connection */
203227
{
204228
QgsDebugMsg( QStringLiteral( "found existing connection, use the existing one" ) );
205229
db = QSqlDatabase::database( threadSafeConnectionName );
206230
}
231+
locker.unlock();
232+
207233
db.setHostName( host );
208234
db.setPort( port.toInt() );
209235
bool connected = false;
@@ -1759,8 +1785,7 @@ QString QgsDb2Provider::dbConnectionName( const QString &name )
17591785
// Starting with Qt 5.11, sharing the same connection between threads is not allowed.
17601786
// We use a dedicated connection for each thread requiring access to the database,
17611787
// using the thread address as connection name.
1762-
const QString threadAddress = QStringLiteral( ":0x%1" ).arg( QString::number( reinterpret_cast< quintptr >( QThread::currentThread() ), 16 ) );
1763-
return name + threadAddress;
1788+
return QStringLiteral( "%1:0x%2" ).arg( name ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) );
17641789
}
17651790

17661791
#ifdef HAVE_GUI

0 commit comments

Comments
 (0)
Please sign in to comment.