QSqlDatabase: add moveToThread()/currentThread()

Add QSqlDatabase::moveToThread() to be able to move the driver instance
to another thread.

[ChangeLog][Sql][QSqLDatabase] QSqlDatabase gained two new functions
moveToThread() and currentThread() to be able to use it in another
thread than the one it was created in.

Fixes: QTBUG-39957
Change-Id: I9cb51358f73a3a2fa72813bfdbe059279d388bd7
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Christian Ehrlicher 2023-02-12 20:09:16 +01:00
parent 46ad7fe966
commit b4c63b89df
3 changed files with 81 additions and 0 deletions

View File

@ -7,6 +7,7 @@
#include "qcoreapplication.h"
#include "qreadwritelock.h"
#include "qsqldriver.h"
#include "qsqldriver_p.h"
#include "qsqldriverplugin.h"
#include "qsqlindex.h"
#include "QtCore/qapplicationstatic.h"
@ -282,6 +283,10 @@ void QSqlDatabasePrivate::disable()
QSqlDriver. Alternatively, you can subclass your own database
driver from QSqlDriver. See \l{How to Write Your Own Database
Driver} for more information.
A QSqlDatabase instance must only be accessed by the thread it
was created in. Therefore you have to make sure to create them
in the correct context. Alternatively you can change the context
with QSqlDatabase::moveToThread().
Create a connection (i.e., an instance of QSqlDatabase) by calling
one of the static addDatabase() functions, where you specify
@ -1333,6 +1338,50 @@ QSql::NumericalPrecisionPolicy QSqlDatabase::numericalPrecisionPolicy() const
return d->precisionPolicy;
}
/*!
\since 6.8
Changes the thread affinity for QSqlDatabase and its associated driver.
This function returns \c true when the function succeeds. Event processing
will continue in the \a targetThread.
During this operation you have to make sure that there is no QSqlQuery
bound to this instance otherwise the QSqlDatabase will not be moved to
the given thread and the function returns \c false.
Since the associated driver is derived from QObject, all constraints for
moving a QObject to another thread also apply to this function.
\sa QObject::moveToThread(), {Threads and the SQL Module}
*/
bool QSqlDatabase::moveToThread(QThread *targetThread)
{
if (auto drv = driver()) {
if (drv != QSqlDatabasePrivate::shared_null()->driver) {
// two instances are alive - the one here and the one in dbDict()
if (d->ref.loadRelaxed() > 2) {
qWarning("QSqlDatabasePrivate::moveToThread: connection '%ls' is still in use "
"in the current thread.", qUtf16Printable(d->connName));
return false;
}
return drv->moveToThread(targetThread);
}
}
return false;
}
/*!
\since 6.8
Returns a pointer to the associated QThread instance.
*/
QThread *QSqlDatabase::currentThread() const
{
if (auto drv = driver())
return drv->thread();
return nullptr;
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QSqlDatabase &d)

View File

@ -18,6 +18,7 @@ class QSqlIndex;
class QSqlRecord;
class QSqlQuery;
class QSqlDatabasePrivate;
class QThread;
class Q_SQL_EXPORT QSqlDriverCreatorBase
{
@ -80,6 +81,8 @@ public:
QString connectionName() const;
void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy);
QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const;
bool moveToThread(QThread *targetThread);
QThread *currentThread() const;
QSqlDriver* driver() const;

View File

@ -104,6 +104,8 @@ private slots:
void infinityAndNan();
void multipleThreads_data() { generic_data(); }
void multipleThreads();
void moveToThread_data() { generic_data(); }
void moveToThread();
void db2_valueCacheUpdate_data() { generic_data("QDB2"); }
void db2_valueCacheUpdate();
@ -2335,5 +2337,32 @@ void tst_QSqlDatabase::multipleThreads()
QTRY_VERIFY(t.isFinished());
}
void tst_QSqlDatabase::moveToThread()
{
QFETCH(QString, dbName);
QSqlDatabase db = QSqlDatabase::database(dbName);
auto clonedDb = QSqlDatabase::cloneDatabase(db, "clonedDb");
auto mainThread = QThread::currentThread();
CHECK_DATABASE(db);
QCOMPARE(db.currentThread(), mainThread);
QCOMPARE(clonedDb.currentThread(), mainThread);
std::unique_ptr<QThread> t(QThread::create([&] {
db.moveToThread(mainThread);
QThread::currentThread()->exit();
}));
db.moveToThread(t.get());
QCOMPARE(db.currentThread(), t.get());
QCOMPARE(clonedDb.currentThread(), mainThread);
t->start();
QTRY_VERIFY(t->isRunning());
QTRY_VERIFY(t->wait(30000));
QCOMPARE(db.currentThread(), mainThread);
QCOMPARE(clonedDb.currentThread(), mainThread);
db = QSqlDatabase();
clonedDb = QSqlDatabase();
QSqlDatabase::removeDatabase("clonedDb");
}
QTEST_MAIN(tst_QSqlDatabase)
#include "tst_qsqldatabase.moc"