Commit b9f8dbfe authored by Marcel Wiesweg's avatar Marcel Wiesweg
Browse files

Rewrite per-thread database connection cleanup

Use QThreadStorage of a per-thread DatabaseThreadData object which is destroyed when the thread finishes.
parent 1831022c
......@@ -79,22 +79,64 @@ public:
}
};
DatabaseThreadData::DatabaseThreadData()
: valid(0),
transactionCount(0)
{
}
DatabaseThreadData::~DatabaseThreadData()
{
if (transactionCount)
{
kDebug() << "WARNING !!! Transaction count is" << transactionCount << "when destroying database!!!";
}
closeDatabase();
}
void DatabaseThreadData::closeDatabase()
{
QString connectionToRemove;
if (database.isOpen())
{
connectionToRemove = database.connectionName();
}
// Destroy object
database = QSqlDatabase();
valid = 0;
transactionCount = 0;
lastError = QSqlError();
// Remove connection
if (!connectionToRemove.isNull())
{
QSqlDatabase::removeDatabase(connectionToRemove);
}
}
DatabaseCoreBackendPrivate::DatabaseCoreBackendPrivate(DatabaseCoreBackend* const backend)
: q(backend)
: currentValidity(0),
isInTransaction(false),
status(DatabaseCoreBackend::Unavailable),
lock(0),
operationStatus(DatabaseCoreBackend::ExecuteNormal),
errorLockOperationStatus(DatabaseCoreBackend::ExecuteNormal),
errorHandler(0),
q(backend)
{
status = DatabaseCoreBackend::Unavailable;
isInTransaction = false;
operationStatus = DatabaseCoreBackend::ExecuteNormal;
errorHandler = 0;
lock = 0;
errorLockOperationStatus = DatabaseCoreBackend::ExecuteNormal;
}
void DatabaseCoreBackendPrivate::init(const QString& name, DatabaseLocking* const l)
DatabaseCoreBackendPrivate::~DatabaseCoreBackendPrivate()
{
QObject::connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
q, SLOT(slotMainThreadFinished()));
// Must be shut down from the main thread.
// Clean up the QThreadStorage. It deletes any stored data.
threadDataStorage.setLocalData(0);
}
void DatabaseCoreBackendPrivate::init(const QString& name, DatabaseLocking* const l)
{
backendName = name;
lock = l;
......@@ -110,82 +152,43 @@ void DatabaseCoreBackendPrivate::init(const QString& name, DatabaseLocking* cons
// finishing of the thread.
QSqlDatabase DatabaseCoreBackendPrivate::databaseForThread()
{
QThread* const thread = QThread::currentThread();
QSqlDatabase db = threadDatabases[thread];
int isValid = databasesValid[thread];
if (!isValid || !db.isOpen())
DatabaseThreadData* threadData = 0;
if (!threadDataStorage.hasLocalData())
{
// need to open a db for thread
bool success = open(db);
if (!success)
{
kDebug() << "Error while opening the database. Details: [" << db.lastError() << "]";
}
QObject::connect(thread, SIGNAL(finished()),
q, SLOT(slotThreadFinished()));
threadData = new DatabaseThreadData;
threadDataStorage.setLocalData(threadData);
}
#ifdef DATABASCOREBACKEND_DEBUG
else
{
kDebug() << "Database ["<< connectionName(thread) <<"] already open for thread ["<< thread <<"].";
threadData = threadDataStorage.localData();
}
#endif
return db;
}
void DatabaseCoreBackendPrivate::closeDatabaseForThread()
{
QThread* const thread = QThread::currentThread();
// do we need to reopen the database because parameter changed and validity was increased?
if (threadData->valid && threadData->valid < currentValidity)
{
threadData->closeDatabase();
}
// scope, so that db is destructed when calling removeDatabase
if (!threadData->valid || !threadData->database.isOpen())
{
QSqlDatabase db = threadDatabases[thread];
threadData->database = createDatabaseConnection();
if (db.isValid())
if (threadData->database.open())
{
threadData->valid = currentValidity;
}
else
{
db.close();
kDebug() << "Error while opening the database. Error was" << threadData->database.lastError();
}
}
threadDatabases.remove(thread);
databaseErrors.remove(thread);
databasesValid[thread] = 0;
transactionCount.remove(thread);
QSqlDatabase::removeDatabase(connectionName(thread));
}
QSqlError DatabaseCoreBackendPrivate::databaseErrorForThread()
{
QThread* const thread = QThread::currentThread();
return databaseErrors[thread];
}
void DatabaseCoreBackendPrivate::setDatabaseErrorForThread(const QSqlError& lastError)
{
QThread* const thread = QThread::currentThread();
databaseErrors.insert(thread, lastError);
}
QString DatabaseCoreBackendPrivate::connectionName(QThread* const thread)
{
return backendName + QString::number((quintptr)thread);
return threadData->database;
}
bool DatabaseCoreBackendPrivate::open(QSqlDatabase& db)
QSqlDatabase DatabaseCoreBackendPrivate::createDatabaseConnection()
{
if (db.isValid())
{
db.close();
}
QThread* const thread = QThread::currentThread();
db = QSqlDatabase::addDatabase(parameters.databaseType, connectionName(thread));
QSqlDatabase db = QSqlDatabase::addDatabase(parameters.databaseType, connectionName());
QString connectOptions = parameters.connectOptions;
if (parameters.isSQLite())
......@@ -211,46 +214,47 @@ bool DatabaseCoreBackendPrivate::open(QSqlDatabase& db)
db.setUserName(parameters.userName);
db.setPassword(parameters.password);
bool success = db.open();
return db;
}
if (success==false)
void DatabaseCoreBackendPrivate::closeDatabaseForThread()
{
if (threadDataStorage.hasLocalData())
{
kDebug() << "Error while opening the database. Error was <" << db.lastError() << ">";
threadDataStorage.localData()->closeDatabase();
}
threadDatabases[thread] = db;
databasesValid[thread] = 1;
transactionCount[thread] = 0;
return success;
}
bool DatabaseCoreBackendPrivate::incrementTransactionCount()
QSqlError DatabaseCoreBackendPrivate::databaseErrorForThread()
{
QThread* const thread = QThread::currentThread();
return (!transactionCount[thread]++);
if (threadDataStorage.hasLocalData())
{
return threadDataStorage.localData()->lastError;
}
return QSqlError();
}
bool DatabaseCoreBackendPrivate::decrementTransactionCount()
void DatabaseCoreBackendPrivate::setDatabaseErrorForThread(const QSqlError& lastError)
{
QThread* const thread = QThread::currentThread();
return (!--transactionCount[thread]);
if (threadDataStorage.hasLocalData())
{
threadDataStorage.localData()->lastError = lastError;
}
}
bool DatabaseCoreBackendPrivate::isInTransactionInOtherThread() const
QString DatabaseCoreBackendPrivate::connectionName()
{
QThread* const thread = QThread::currentThread();
QHash<QThread*, int>::const_iterator it;
return backendName + QString::number((quintptr)QThread::currentThread());
}
for (it = transactionCount.constBegin(); it != transactionCount.constEnd(); ++it)
{
if (it.key() != thread && it.value())
{
return true;
}
}
bool DatabaseCoreBackendPrivate::incrementTransactionCount()
{
return (!threadDataStorage.localData()->transactionCount++);
}
return false;
bool DatabaseCoreBackendPrivate::decrementTransactionCount()
{
return (!--threadDataStorage.localData()->transactionCount);
}
bool DatabaseCoreBackendPrivate::isInMainThread() const
......@@ -740,18 +744,6 @@ void DatabaseCoreBackend::setDatabaseErrorHandler(DatabaseErrorHandler* const ha
d->errorHandler = handler;
}
void DatabaseCoreBackend::slotThreadFinished()
{
Q_D(DatabaseCoreBackend);
d->closeDatabaseForThread();
}
void DatabaseCoreBackend::slotMainThreadFinished()
{
Q_D(DatabaseCoreBackend);
d->closeDatabaseForThread();
}
bool DatabaseCoreBackend::isCompatible(const DatabaseParameters& parameters)
{
return QSqlDatabase::drivers().contains(parameters.databaseType);
......@@ -761,10 +753,8 @@ bool DatabaseCoreBackend::open(const DatabaseParameters& parameters)
{
Q_D(DatabaseCoreBackend);
d->parameters = parameters;
// Force possibly opened thread dbs to re-open with new parameters.
// They are not accessible from this thread!
d->databasesValid.clear();
// This will make possibly opened thread dbs reload at next access
d->currentValidity++;
int retries = 0;
......@@ -1634,7 +1624,7 @@ DatabaseCoreBackend::QueryState DatabaseCoreBackend::commitTransaction()
bool DatabaseCoreBackend::isInTransaction() const
{
Q_D(const DatabaseCoreBackend);
return d->isInTransactionInOtherThread();
return d->isInTransaction;
}
void DatabaseCoreBackend::rollbackTransaction()
......
......@@ -472,11 +472,6 @@ public:
LastInsertId
*/
private Q_SLOTS:
void slotThreadFinished();
void slotMainThreadFinished();
protected:
DatabaseCoreBackendPrivate* const d_ptr;
......
......@@ -29,6 +29,7 @@
#include <QHash>
#include <QSqlDatabase>
#include <QThread>
#include <QThreadStorage>
#include <QWaitCondition>
// Local includes
......@@ -40,25 +41,38 @@
namespace Digikam
{
class DatabaseThreadData
{
public:
DatabaseThreadData();
~DatabaseThreadData();
void closeDatabase();
QSqlDatabase database;
int valid;
int transactionCount;
QSqlError lastError;
};
class DIGIKAM_EXPORT DatabaseCoreBackendPrivate : public DatabaseErrorAnswer
{
public:
explicit DatabaseCoreBackendPrivate(DatabaseCoreBackend* const backend);
virtual ~DatabaseCoreBackendPrivate()
{
}
virtual ~DatabaseCoreBackendPrivate();
void init(const QString& connectionName, DatabaseLocking* const locking);
QString connectionName(QThread* const thread);
QString connectionName();
QSqlDatabase databaseForThread();
QSqlError databaseErrorForThread();
void setDatabaseErrorForThread(const QSqlError& lastError);
QSqlDatabase createDatabaseConnection();
void closeDatabaseForThread();
bool open(QSqlDatabase& db);
bool incrementTransactionCount();
bool decrementTransactionCount();
bool isInTransactionInOtherThread() const;
......@@ -88,14 +102,10 @@ public:
public:
// this is always accessed in mutex context, no need for QThreadStorage
QHash<QThread*, QSqlDatabase> threadDatabases;
// this is not only db.isValid(), but also "parameters changed, need to reopen"
QHash<QThread*, int> databasesValid;
// for recursive transactions
QHash<QThread*, int> transactionCount;
QThreadStorage<DatabaseThreadData*> threadDataStorage;
QHash<QThread*, QSqlError> databaseErrors;
// This compares to DatabaseThreadData's valid. If currentValidity is increased and > valid, the db is marked as invalid
int currentValidity;
bool isInTransaction;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment