summaryrefslogtreecommitdiffstats
path: root/src/util/db
diff options
context:
space:
mode:
authorUwe Klotz <uwe_klotz@web.de>2017-06-10 09:21:00 +0200
committerUwe Klotz <uwe_klotz@web.de>2017-06-12 07:39:47 +0200
commit40ccb3dbee146d9750c1b20e82a0c9a2b5b93ac4 (patch)
tree97478a4e7ba06eb847acde95a5e2dafae1d74bec /src/util/db
parent2e1d15d306cc39f3b9f0a9b60d4efd4a92fe4829 (diff)
Manage a pool of thread-local database connections
Diffstat (limited to 'src/util/db')
-rw-r--r--src/util/db/dbconnectionpool.cpp106
-rw-r--r--src/util/db/dbconnectionpool.h86
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