diff options
author | Uwe Klotz <uwe_klotz@web.de> | 2017-06-10 09:21:00 +0200 |
---|---|---|
committer | Uwe Klotz <uwe_klotz@web.de> | 2017-06-12 07:39:47 +0200 |
commit | 40ccb3dbee146d9750c1b20e82a0c9a2b5b93ac4 (patch) | |
tree | 97478a4e7ba06eb847acde95a5e2dafae1d74bec /src/util/db | |
parent | 2e1d15d306cc39f3b9f0a9b60d4efd4a92fe4829 (diff) |
Manage a pool of thread-local database connections
Diffstat (limited to 'src/util/db')
-rw-r--r-- | src/util/db/dbconnectionpool.cpp | 106 | ||||
-rw-r--r-- | src/util/db/dbconnectionpool.h | 86 |
2 files changed, 192 insertions, 0 deletions
diff --git a/src/util/db/dbconnectionpool.cpp b/src/util/db/dbconnectionpool.cpp new file mode 100644 index 0000000000..3dc51454fe --- /dev/null +++ b/src/util/db/dbconnectionpool.cpp @@ -0,0 +1,106 @@ +#include "util/db/dbconnectionpool.h" + +#include "util/memory.h" +#include "util/logger.h" + + +namespace mixxx { + +namespace { + +const mixxx::Logger kLogger("DbConnectionPool"); + +} // anonymous namespace + +bool DbConnectionPool::createThreadLocalConnection() { + VERIFY_OR_DEBUG_ASSERT(!m_threadLocalConnections.hasLocalData()) { + DEBUG_ASSERT(m_threadLocalConnections.localData()); + kLogger.critical() + << "Thread-local database connection already exists" + << *m_threadLocalConnections.localData(); + return false; // abort + } + const int connectionIndex = + m_connectionCounter.fetchAndAddAcquire(1) + 1; + const QString indexedConnectionName = + QString("%1-%2").arg( + m_prototypeConnection.database().connectionName(), + QString::number(connectionIndex)); + auto pConnection = std::make_unique<DbConnection>(m_prototypeConnection, indexedConnectionName); + if (!pConnection->open()) { + kLogger.critical() + << "Failed to open thread-local database connection" + << *pConnection; + return false; // abort + } + m_threadLocalConnections.setLocalData(pConnection.get()); // transfer ownership + pConnection.release(); // release ownership + DEBUG_ASSERT(m_threadLocalConnections.hasLocalData()); + DEBUG_ASSERT(m_threadLocalConnections.localData()); + kLogger.info() + << "Cloned thread-local database connection" + << *m_threadLocalConnections.localData(); + return true; +} + +void DbConnectionPool::destroyThreadLocalConnection() { + m_threadLocalConnections.setLocalData(nullptr); +} + +QSqlDatabase DbConnectionPool::threadLocalDatabase() const { + VERIFY_OR_DEBUG_ASSERT(m_threadLocalConnections.hasLocalData()) { + kLogger.critical() + << "Thread-local database connection not available"; + } + DbConnection* pConnection = m_threadLocalConnections.localData(); + VERIFY_OR_DEBUG_ASSERT(pConnection) { + kLogger.critical() + << "Thread-local database connection not valid"; + return QSqlDatabase(); // abort + } + kLogger.debug() + << "Found thread-local database connection" + << *pConnection;; + return pConnection->database(); +} + +DbConnectionPool::DbConnectionPool( + const QDir& dirPath, + const QString& fileName, + const QString& connectionName) + : m_prototypeConnection(dirPath, fileName, connectionName), + m_connectionCounter(0) { +} + +DbConnectionPool::ThreadLocalScope::ThreadLocalScope( + DbConnectionPoolPtr pDbConnectionPool) { + if (pDbConnectionPool && pDbConnectionPool->createThreadLocalConnection()) { + m_pDbConnectionPool = std::move(pDbConnectionPool); + } +} + +DbConnectionPool::ThreadLocalScope::ThreadLocalScope( + ThreadLocalScope&& other) + : m_pDbConnectionPool(std::move(other.m_pDbConnectionPool)) { + DEBUG_ASSERT(!other.m_pDbConnectionPool); +} + +DbConnectionPool::ThreadLocalScope::~ThreadLocalScope() { + if (m_pDbConnectionPool) { + m_pDbConnectionPool->destroyThreadLocalConnection(); + } +} + +DbConnectionPool::ThreadLocalScope& DbConnectionPool::ThreadLocalScope::operator=( + ThreadLocalScope&& other) { + if (this != &other) { + if (m_pDbConnectionPool) { + m_pDbConnectionPool->destroyThreadLocalConnection(); + } + m_pDbConnectionPool = std::move(other.m_pDbConnectionPool); + DEBUG_ASSERT(!other.m_pDbConnectionPool); + } + return *this; +} + +} // namespace mixxx diff --git a/src/util/db/dbconnectionpool.h b/src/util/db/dbconnectionpool.h new file mode 100644 index 0000000000..ba482bc7b8 --- /dev/null +++ b/src/util/db/dbconnectionpool.h @@ -0,0 +1,86 @@ +#ifndef MIXXX_DBCONNECTIONPOOL_H +#define MIXXX_DBCONNECTIONPOOL_H + + +#include <QAtomicInt> +#include <QThreadStorage> + +#include "util/db/dbconnection.h" +#include "util/memory.h" +#include "util/assert.h" + + +namespace mixxx { + +class DbConnectionPool; +typedef std::shared_ptr<DbConnectionPool> DbConnectionPoolPtr; + +class DbConnectionPool final { + public: + static DbConnectionPoolPtr create( + const QDir& dirPath, + const QString& fileName, + const QString& connectionName) { + return std::make_shared<DbConnectionPool>(dirPath, fileName, connectionName); + } + + DbConnectionPool( + const QDir& dirPath, + const QString& fileName, + const QString& connectionName); + + // Returns a database connection for the current thread, that has + // previously been created (see above). The returned connection + // is only valid within the current thread! + QSqlDatabase threadLocalDatabase() const; + + // Temporarily creates and destroys a thread-local database connection + class ThreadLocalScope final { + public: + explicit ThreadLocalScope( + DbConnectionPoolPtr pDbConnectionPool = DbConnectionPoolPtr()); + ThreadLocalScope( + ThreadLocalScope&& other); + ~ThreadLocalScope(); + + ThreadLocalScope& operator=( + ThreadLocalScope&& other); + + operator bool() const { + return m_pDbConnectionPool != nullptr; + } + + QSqlDatabase database() const { + VERIFY_OR_DEBUG_ASSERT(m_pDbConnectionPool) { + return QSqlDatabase(); // safety fallback + } + return m_pDbConnectionPool->threadLocalDatabase(); + } + + private: + ThreadLocalScope(const ThreadLocalScope&) = delete; + ThreadLocalScope& operator=(const ThreadLocalScope&) = delete; + + DbConnectionPoolPtr m_pDbConnectionPool; + }; + + private: + DbConnectionPool(const DbConnectionPool&) = delete; + DbConnectionPool(const DbConnectionPool&&) = delete; + + friend class ThreadLocalScope; + bool createThreadLocalConnection(); + void destroyThreadLocalConnection(); + + const DbConnection m_prototypeConnection; + + QAtomicInt m_connectionCounter; + + QThreadStorage<DbConnection*> m_threadLocalConnections; + +}; + +} // namespace mixxx + + +#endif // MIXXX_DBCONNECTIONPOOL_H |