} // namespace
-
-
-struct KVStore::Transaction {
- Transaction(KVStore* kvstorePtr)
+struct KVStore::TransactionImpl {
+ TransactionImpl(KVStore* kvstorePtr)
: mKVStorePtr(kvstorePtr)
{
- if (mKVStorePtr->mTransactionCounter == 0) {
- mKVStorePtr->mConn.exec("BEGIN EXCLUSIVE TRANSACTION");
- }
- mKVStorePtr->mTransactionCounter += 1;
+ mKVStorePtr->mConnMtx.lock();
+ mKVStorePtr->mConn.exec("BEGIN EXCLUSIVE TRANSACTION");
}
- ~Transaction()
+ ~TransactionImpl()
{
- mKVStorePtr->mTransactionCounter -= 1;
- if (mKVStorePtr->mTransactionCounter == 0) {
- if (std::uncaught_exception()) {
+ if (std::uncaught_exception()) {
+ try {
mKVStorePtr->mConn.exec("ROLLBACK TRANSACTION");
- } else {
+ } catch (ConfigException&) {}
+ } else {
+ try {
mKVStorePtr->mConn.exec("COMMIT TRANSACTION");
- }
+ } catch (ConfigException&) {}
}
+ mKVStorePtr->mConnMtx.unlock();
}
private:
config::KVStore* mKVStorePtr;
};
+KVStore::Transaction KVStore::getTransaction()
+{
+ Lock lock(getTransactionMutex);
+
+ auto t = mTransactionImplPtr.lock();
+ if (!t) {
+ t = std::make_shared<TransactionImpl>(this);
+ mTransactionImplPtr = t;
+ }
+ return t;
+}
KVStore::KVStore(const std::string& path)
: mPath(path),
void KVStore::setupDb()
{
- Lock lock(mConnMtx);
- Transaction transaction(this);
+ Transaction transaction = getTransaction();
mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
}
void KVStore::clear()
{
- Lock lock(mConnMtx);
- Transaction transaction(this);
+ Transaction transaction = getTransaction();
mConn.exec("DELETE FROM data");
}
bool KVStore::isEmpty()
{
- Lock lock(mConnMtx);
+ Transaction transaction = getTransaction();
ScopedReset scopedReset(mGetIsEmptyStmt);
int ret = ::sqlite3_step(mGetIsEmptyStmt->get());
bool KVStore::exists(const std::string& key)
{
- Lock lock(mConnMtx);
+ Transaction transaction = getTransaction();
ScopedReset scopedReset(mGetKeyExistsStmt);
::sqlite3_bind_text(mGetKeyExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
void KVStore::remove(const std::string& key)
{
- Lock lock(mConnMtx);
- Transaction transaction(this);
- removeInternal(key);
-}
-
-void KVStore::removeInternal(const std::string& key)
-{
+ Transaction transaction = getTransaction();
ScopedReset scopedReset(mRemoveValuesStmt);
::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
void KVStore::setInternal(const std::string& key, const std::string& value)
{
- Lock lock(mConnMtx);
+ Transaction transaction = getTransaction();
+ ScopedReset scopedReset(mSetValueStmt);
::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
- Transaction transaction(this);
- ScopedReset scopedReset(mSetValueStmt);
if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
throw ConfigException("Too many values to insert");
}
- Lock lock(mConnMtx);
- Transaction transaction(this);
+ Transaction transaction = getTransaction();
- removeInternal(key);
+ remove(key);
// Save vector's capacity
setInternal(key, values.size());
std::string KVStore::getInternal(const std::string& key, std::string*)
{
- Lock lock(mConnMtx);
+ Transaction transaction = getTransaction();
+ ScopedReset scopedReset(mGetValueStmt);
::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
- Transaction transaction(this);
- ScopedReset scopedReset(mGetValueStmt);
-
int ret = ::sqlite3_step(mGetValueStmt->get());
if (ret == SQLITE_DONE) {
throw ConfigException("No value corresponding to the key: " + key);
std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
{
- Lock lock(mConnMtx);
- Transaction transaction(this);
+ Transaction transaction = getTransaction();
unsigned int valuesSize = get<unsigned int>(key);
std::vector<std::string> values(valuesSize);
#include <sstream>
#include <string>
#include <vector>
+#include <atomic>
namespace config {
class KVStore {
public:
+ /**
+ * A guard struct for thread synchronization and transaction management.
+ */
+ typedef std::shared_ptr<void> Transaction;
/**
* @param path configuration database file path
return getInternal(key, static_cast<T*>(nullptr));
}
+ KVStore::Transaction getTransaction();
private:
- struct Transaction;
- typedef std::lock_guard<std::recursive_mutex> Lock;
- unsigned int mTransactionCounter;
+ typedef std::lock_guard<std::mutex> Lock;
+
+ struct TransactionImpl;
+ std::weak_ptr<TransactionImpl> mTransactionImplPtr;
+ std::mutex getTransactionMutex;
+ std::mutex mConnMtx;
void setInternal(const std::string& key, const std::string& value);
void setInternal(const std::string& key, const std::initializer_list<std::string>& values);
template<typename T>
std::vector<T> getInternal(const std::string& key, std::vector<T>*);
- std::recursive_mutex mConnMtx;
-
std::string mPath;
sqlite3::Connection mConn;
std::unique_ptr<sqlite3::Statement> mGetValueStmt;
void setupDb();
void prepareStatements();
void createFunctions();
-
- void removeInternal(const std::string& key);
-
};
namespace {