summaryrefslogtreecommitdiffstats
path: root/common/storage_lmdb.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2018-01-03 15:34:35 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-01-03 16:11:38 +0100
commit9fa528c5fceefd53303a604625d13cf0cdbb109e (patch)
tree8f5d6c64300f54af1c6d3afcf5ba8b0cd82a4492 /common/storage_lmdb.cpp
parent46bf82eaec0a30281d0b8deaf1ffbd06030eb997 (diff)
downloadsink-9fa528c5fceefd53303a604625d13cf0cdbb109e.tar.gz
sink-9fa528c5fceefd53303a604625d13cf0cdbb109e.zip
Use read-write locks for finer grained control to sDbi and sEnvironments
There are only a few cases where have to access the list of dbis or environments, so we can normally get away with just read-locking. This seems to fix a segfault that was possibly caused be an environment being reused that has already been freed in another thread. The read-only lock when initially retrieving the environment seems to fix that.
Diffstat (limited to 'common/storage_lmdb.cpp')
-rw-r--r--common/storage_lmdb.cpp37
1 files changed, 21 insertions, 16 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 600fbd1..514a4ea 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -23,28 +23,26 @@
23 23
24#include <iostream> 24#include <iostream>
25 25
26#include <QAtomicInt>
27#include <QDebug> 26#include <QDebug>
28#include <QDir> 27#include <QDir>
29#include <QReadWriteLock> 28#include <QReadWriteLock>
30#include <QString> 29#include <QString>
31#include <QTime> 30#include <QTime>
32#include <QMutex>
33#include <valgrind.h> 31#include <valgrind.h>
34
35#include <lmdb.h> 32#include <lmdb.h>
36#include "log.h" 33#include "log.h"
37 34
38namespace Sink { 35namespace Sink {
39namespace Storage { 36namespace Storage {
40 37
41 //TODO a QReadWriteLock would be a better choice because we mostly do reads. 38extern QReadWriteLock sDbisLock;
42extern QMutex sMutex; 39extern QReadWriteLock sEnvironmentsLock;
43extern QHash<QString, MDB_env *> sEnvironments; 40extern QHash<QString, MDB_env *> sEnvironments;
44extern QHash<QString, MDB_dbi> sDbis; 41extern QHash<QString, MDB_dbi> sDbis;
45 42
46 43
47QMutex sMutex; 44QReadWriteLock sDbisLock;
45QReadWriteLock sEnvironmentsLock;
48QHash<QString, MDB_env *> sEnvironments; 46QHash<QString, MDB_env *> sEnvironments;
49QHash<QString, MDB_dbi> sDbis; 47QHash<QString, MDB_dbi> sDbis;
50 48
@@ -558,6 +556,7 @@ public:
558 void startTransaction() 556 void startTransaction()
559 { 557 {
560 Q_ASSERT(!transaction); 558 Q_ASSERT(!transaction);
559 Q_ASSERT(sEnvironments.values().contains(env));
561 // auto f = [](const char *msg, void *ctx) -> int { 560 // auto f = [](const char *msg, void *ctx) -> int {
562 // qDebug() << msg; 561 // qDebug() << msg;
563 // return 0; 562 // return 0;
@@ -636,7 +635,7 @@ bool DataStore::Transaction::commit(const std::function<void(const DataStore::Er
636 //Add the created dbis to the shared environment 635 //Add the created dbis to the shared environment
637 if (!d->createdDbs.isEmpty()) { 636 if (!d->createdDbs.isEmpty()) {
638 if (!d->noLock) { 637 if (!d->noLock) {
639 sMutex.lock(); 638 sDbisLock.lockForWrite();
640 } 639 }
641 for (auto it = d->createdDbs.constBegin(); it != d->createdDbs.constEnd(); it++) { 640 for (auto it = d->createdDbs.constBegin(); it != d->createdDbs.constEnd(); it++) {
642 Q_ASSERT(!sDbis.contains(it.key())); 641 Q_ASSERT(!sDbis.contains(it.key()));
@@ -644,7 +643,7 @@ bool DataStore::Transaction::commit(const std::function<void(const DataStore::Er
644 } 643 }
645 d->createdDbs.clear(); 644 d->createdDbs.clear();
646 if (!d->noLock) { 645 if (!d->noLock) {
647 sMutex.unlock(); 646 sDbisLock.unlock();
648 } 647 }
649 } 648 }
650 649
@@ -698,18 +697,17 @@ DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &
698 d->implicitCommit = true; 697 d->implicitCommit = true;
699 auto p = new DataStore::NamedDatabase::Private(db, allowDuplicates, d->defaultErrorHandler, d->name, d->transaction); 698 auto p = new DataStore::NamedDatabase::Private(db, allowDuplicates, d->defaultErrorHandler, d->name, d->transaction);
700 if (!d->noLock) { 699 if (!d->noLock) {
701 //This wouldn't be necessary with a read-write lock, we could read-only lock here 700 sDbisLock.lockForRead();
702 sMutex.lock();
703 } 701 }
704 if (!p->openDatabase(d->requestedRead, errorHandler)) { 702 if (!p->openDatabase(d->requestedRead, errorHandler)) {
705 if (!d->noLock) { 703 if (!d->noLock) {
706 sMutex.unlock(); 704 sDbisLock.unlock();
707 } 705 }
708 delete p; 706 delete p;
709 return DataStore::NamedDatabase(); 707 return DataStore::NamedDatabase();
710 } 708 }
711 if (!d->noLock) { 709 if (!d->noLock) {
712 sMutex.unlock(); 710 sDbisLock.unlock();
713 } 711 }
714 if (p->createdNewDbi) { 712 if (p->createdNewDbi) {
715 d->createdDbs.insert(p->createdDbName, p->dbi); 713 d->createdDbs.insert(p->createdDbName, p->dbi);
@@ -813,8 +811,11 @@ public:
813 void initEnvironment(const QString &fullPath, const DbLayout &layout) 811 void initEnvironment(const QString &fullPath, const DbLayout &layout)
814 { 812 {
815 // Ensure the environment is only created once, and that we only have one environment per process 813 // Ensure the environment is only created once, and that we only have one environment per process
814 QReadLocker locker(&sEnvironmentsLock);
816 if (!(env = sEnvironments.value(fullPath))) { 815 if (!(env = sEnvironments.value(fullPath))) {
817 QMutexLocker locker(&sMutex); 816 locker.unlock();
817 QWriteLocker envLocker(&sEnvironmentsLock);
818 QWriteLocker dbiLocker(&sDbisLock);
818 if (!(env = sEnvironments.value(fullPath))) { 819 if (!(env = sEnvironments.value(fullPath))) {
819 int rc = 0; 820 int rc = 0;
820 if ((rc = mdb_env_create(&env))) { 821 if ((rc = mdb_env_create(&env))) {
@@ -932,7 +933,10 @@ DataStore::Transaction DataStore::createTransaction(AccessMode type, const std::
932 errorHandler(Error(d->name.toLatin1(), ErrorCodes::GenericError, "Failed to create transaction: Requested read/write transaction in read-only mode.")); 933 errorHandler(Error(d->name.toLatin1(), ErrorCodes::GenericError, "Failed to create transaction: Requested read/write transaction in read-only mode."));
933 return Transaction(); 934 return Transaction();
934 } 935 }
935 936 QReadLocker locker(&sEnvironmentsLock);
937 if (!sEnvironments.values().contains(d->env)) {
938 return {};
939 }
936 return Transaction(new Transaction::Private(requestedRead, defaultErrorHandler(), d->name, d->env)); 940 return Transaction(new Transaction::Private(requestedRead, defaultErrorHandler(), d->name, d->env));
937} 941}
938 942
@@ -948,7 +952,8 @@ qint64 DataStore::diskUsage() const
948void DataStore::removeFromDisk() const 952void DataStore::removeFromDisk() const
949{ 953{
950 const QString fullPath(d->storageRoot + '/' + d->name); 954 const QString fullPath(d->storageRoot + '/' + d->name);
951 QMutexLocker locker(&sMutex); 955 QWriteLocker dbiLocker(&sDbisLock);
956 QWriteLocker envLocker(&sEnvironmentsLock);
952 SinkTrace() << "Removing database from disk: " << fullPath; 957 SinkTrace() << "Removing database from disk: " << fullPath;
953 sEnvironments.take(fullPath); 958 sEnvironments.take(fullPath);
954 for (const auto &key : sDbis.keys()) { 959 for (const auto &key : sDbis.keys()) {
@@ -967,7 +972,7 @@ void DataStore::removeFromDisk() const
967 972
968void DataStore::clearEnv() 973void DataStore::clearEnv()
969{ 974{
970 QMutexLocker locker(&sMutex); 975 QWriteLocker locker(&sEnvironmentsLock);
971 for (auto env : sEnvironments) { 976 for (auto env : sEnvironments) {
972 mdb_env_close(env); 977 mdb_env_close(env);
973 } 978 }