Apply uniform formatting via clang-format
This commit is contained in:
parent
ff1fc3ebd4
commit
2e7d278c9b
|
@ -30,3 +30,6 @@
|
||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.clang-format
|
||||||
|
|
|
@ -2,37 +2,35 @@
|
||||||
|
|
||||||
#include "./lmdb-safe.hh"
|
#include "./lmdb-safe.hh"
|
||||||
|
|
||||||
#include <boost/archive/binary_oarchive.hpp>
|
|
||||||
#include <boost/archive/binary_iarchive.hpp>
|
#include <boost/archive/binary_iarchive.hpp>
|
||||||
|
#include <boost/archive/binary_oarchive.hpp>
|
||||||
|
|
||||||
#include <boost/serialization/vector.hpp>
|
|
||||||
#include <boost/serialization/string.hpp>
|
#include <boost/serialization/string.hpp>
|
||||||
#include <boost/serialization/utility.hpp>
|
#include <boost/serialization/utility.hpp>
|
||||||
|
#include <boost/serialization/vector.hpp>
|
||||||
|
|
||||||
|
#include <boost/iostreams/device/back_inserter.hpp>
|
||||||
#include <boost/iostreams/stream.hpp>
|
#include <boost/iostreams/stream.hpp>
|
||||||
#include <boost/iostreams/stream_buffer.hpp>
|
#include <boost/iostreams/stream_buffer.hpp>
|
||||||
#include <boost/iostreams/device/back_inserter.hpp>
|
|
||||||
|
|
||||||
namespace LMDBSafe {
|
namespace LMDBSafe {
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> std::string serToString(const T &t)
|
||||||
std::string serToString(const T& t)
|
|
||||||
{
|
{
|
||||||
auto ret = std::string();
|
auto ret = std::string();
|
||||||
auto inserter = boost::iostreams::back_insert_device<std::string>(ret);
|
auto inserter = boost::iostreams::back_insert_device<std::string>(ret);
|
||||||
auto stream = boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>>(inserter);
|
auto stream = boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>>(inserter);
|
||||||
auto oa = boost::archive::binary_oarchive(stream, boost::archive::no_header | boost::archive::no_codecvt);
|
auto oa = boost::archive::binary_oarchive(stream, boost::archive::no_header | boost::archive::no_codecvt);
|
||||||
oa << t;
|
oa << t;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> void serFromString(string_view str, T &ret)
|
||||||
void serFromString(string_view str, T& ret)
|
|
||||||
{
|
{
|
||||||
auto source = boost::iostreams::array_source(str.data(), str.size());
|
auto source = boost::iostreams::array_source(str.data(), str.size());
|
||||||
auto stream = boost::iostreams::stream<boost::iostreams::array_source>(source);
|
auto stream = boost::iostreams::stream<boost::iostreams::array_source>(source);
|
||||||
auto ia = boost::archive::binary_iarchive(stream, boost::archive::no_header|boost::archive::no_codecvt);
|
auto ia = boost::archive::binary_iarchive(stream, boost::archive::no_header | boost::archive::no_codecvt);
|
||||||
ia >> ret;
|
ia >> ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace LMDBSafe
|
||||||
|
|
|
@ -6,31 +6,29 @@
|
||||||
|
|
||||||
#include <reflective-rapidjson/lib/binary/reflector.h>
|
#include <reflective-rapidjson/lib/binary/reflector.h>
|
||||||
|
|
||||||
|
#include <boost/iostreams/device/back_inserter.hpp>
|
||||||
#include <boost/iostreams/stream.hpp>
|
#include <boost/iostreams/stream.hpp>
|
||||||
#include <boost/iostreams/stream_buffer.hpp>
|
#include <boost/iostreams/stream_buffer.hpp>
|
||||||
#include <boost/iostreams/device/back_inserter.hpp>
|
|
||||||
|
|
||||||
namespace LMDBSafe {
|
namespace LMDBSafe {
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> std::string serToString(const T &t)
|
||||||
std::string serToString(const T& t)
|
|
||||||
{
|
{
|
||||||
auto ret = std::string();
|
auto ret = std::string();
|
||||||
auto inserter = boost::iostreams::back_insert_device<std::string>(ret);
|
auto inserter = boost::iostreams::back_insert_device<std::string>(ret);
|
||||||
auto stream = boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>>(inserter);
|
auto stream = boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>>(inserter);
|
||||||
auto deserializer = ReflectiveRapidJSON::BinaryReflector::BinarySerializer(&stream);
|
auto deserializer = ReflectiveRapidJSON::BinaryReflector::BinarySerializer(&stream);
|
||||||
deserializer.write(t);
|
deserializer.write(t);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> void serFromString(string_view str, T &ret)
|
||||||
void serFromString(string_view str, T& ret)
|
|
||||||
{
|
{
|
||||||
auto source = boost::iostreams::array_source(str.data(), str.size());
|
auto source = boost::iostreams::array_source(str.data(), str.size());
|
||||||
auto stream = boost::iostreams::stream<boost::iostreams::array_source>(source);
|
auto stream = boost::iostreams::stream<boost::iostreams::array_source>(source);
|
||||||
auto serializer = ReflectiveRapidJSON::BinaryReflector::BinaryDeserializer(&stream);
|
auto serializer = ReflectiveRapidJSON::BinaryReflector::BinaryDeserializer(&stream);
|
||||||
ret = T();
|
ret = T();
|
||||||
serializer.read(ret);
|
serializer.read(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace LMDBSafe
|
||||||
|
|
417
lmdb-safe.cc
417
lmdb-safe.cc
|
@ -3,369 +3,360 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace LMDBSafe {
|
namespace LMDBSafe {
|
||||||
|
|
||||||
MDBDbi::MDBDbi(MDB_env* env, MDB_txn* txn, const string_view dbname, unsigned int flags)
|
MDBDbi::MDBDbi(MDB_env *env, MDB_txn *txn, const string_view dbname, unsigned int flags)
|
||||||
{
|
{
|
||||||
(void)env;
|
(void)env;
|
||||||
// A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
|
// A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
|
||||||
|
|
||||||
if(const auto rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi))
|
if (const auto rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi))
|
||||||
throw LMDBError("Unable to open named database: ", rc);
|
throw LMDBError("Unable to open named database: ", rc);
|
||||||
|
|
||||||
// Database names are keys in the unnamed database, and may be read but not written.
|
// Database names are keys in the unnamed database, and may be read but not written.
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBEnv::MDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs)
|
MDBEnv::MDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs)
|
||||||
{
|
{
|
||||||
mdb_env_create(&d_env);
|
mdb_env_create(&d_env);
|
||||||
if(const auto rc = mdb_env_set_mapsize(d_env, 16ULL * 4096 * 244140ULL)) { // 4GB
|
if (const auto rc = mdb_env_set_mapsize(d_env, 16ULL * 4096 * 244140ULL)) { // 4GB
|
||||||
throw LMDBError("Setting map size: ", rc);
|
throw LMDBError("Setting map size: ", rc);
|
||||||
}
|
}
|
||||||
// Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(),
|
// Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(),
|
||||||
if (const auto rc = mdb_env_set_maxdbs(d_env, maxDBs)) {
|
if (const auto rc = mdb_env_set_maxdbs(d_env, maxDBs)) {
|
||||||
throw LMDBError("Setting maxdbs: ", rc);
|
throw LMDBError("Setting maxdbs: ", rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need MDB_NOTLS since we rely on its semantics
|
// we need MDB_NOTLS since we rely on its semantics
|
||||||
if(const auto rc = mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) {
|
if (const auto rc = mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) {
|
||||||
// If this function fails, mdb_env_close() must be called to discard the MDB_env handle.
|
// If this function fails, mdb_env_close() must be called to discard the MDB_env handle.
|
||||||
mdb_env_close(d_env);
|
mdb_env_close(d_env);
|
||||||
throw LMDBError("Unable to open database file " + std::string(fname) + ": ", rc);
|
throw LMDBError("Unable to open database file " + std::string(fname) + ": ", rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBEnv::incROTX()
|
void MDBEnv::incROTX()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(d_countmutex);
|
std::lock_guard<std::mutex> l(d_countmutex);
|
||||||
++d_ROtransactionsOut[std::this_thread::get_id()];
|
++d_ROtransactionsOut[std::this_thread::get_id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBEnv::decROTX()
|
void MDBEnv::decROTX()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(d_countmutex);
|
std::lock_guard<std::mutex> l(d_countmutex);
|
||||||
--d_ROtransactionsOut[std::this_thread::get_id()];
|
--d_ROtransactionsOut[std::this_thread::get_id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBEnv::incRWTX()
|
void MDBEnv::incRWTX()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(d_countmutex);
|
std::lock_guard<std::mutex> l(d_countmutex);
|
||||||
++d_RWtransactionsOut[std::this_thread::get_id()];
|
++d_RWtransactionsOut[std::this_thread::get_id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBEnv::decRWTX()
|
void MDBEnv::decRWTX()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(d_countmutex);
|
std::lock_guard<std::mutex> l(d_countmutex);
|
||||||
--d_RWtransactionsOut[std::this_thread::get_id()];
|
--d_RWtransactionsOut[std::this_thread::get_id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
int MDBEnv::getRWTX()
|
int MDBEnv::getRWTX()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(d_countmutex);
|
std::lock_guard<std::mutex> l(d_countmutex);
|
||||||
return d_RWtransactionsOut[std::this_thread::get_id()];
|
return d_RWtransactionsOut[std::this_thread::get_id()];
|
||||||
}
|
}
|
||||||
int MDBEnv::getROTX()
|
int MDBEnv::getROTX()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(d_countmutex);
|
std::lock_guard<std::mutex> l(d_countmutex);
|
||||||
return d_ROtransactionsOut[std::this_thread::get_id()];
|
return d_ROtransactionsOut[std::this_thread::get_id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MDBEnv> getMDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs)
|
||||||
std::shared_ptr<MDBEnv> getMDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs)
|
|
||||||
{
|
{
|
||||||
struct Value
|
struct Value {
|
||||||
{
|
weak_ptr<MDBEnv> wp;
|
||||||
weak_ptr<MDBEnv> wp;
|
unsigned int flags;
|
||||||
unsigned int flags;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
static std::map<tuple<dev_t, ino_t>, Value> s_envs;
|
static std::map<tuple<dev_t, ino_t>, Value> s_envs;
|
||||||
static std::mutex mut;
|
static std::mutex mut;
|
||||||
|
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
if(stat(fname, &statbuf)) {
|
if (stat(fname, &statbuf)) {
|
||||||
if(errno != ENOENT)
|
if (errno != ENOENT)
|
||||||
throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno)));
|
throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno)));
|
||||||
else {
|
else {
|
||||||
std::lock_guard<std::mutex> l(mut);
|
std::lock_guard<std::mutex> l(mut);
|
||||||
auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, maxDBs);
|
auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, maxDBs);
|
||||||
if(stat(fname, &statbuf))
|
if (stat(fname, &statbuf))
|
||||||
throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno)));
|
throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno)));
|
||||||
auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
|
auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
|
||||||
s_envs[key] = {fresh, flags};
|
s_envs[key] = { fresh, flags };
|
||||||
return fresh;
|
return fresh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> l(mut);
|
std::lock_guard<std::mutex> l(mut);
|
||||||
auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
|
auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
|
||||||
auto iter = s_envs.find(key);
|
auto iter = s_envs.find(key);
|
||||||
if(iter != s_envs.end()) {
|
if (iter != s_envs.end()) {
|
||||||
auto sp = iter->second.wp.lock();
|
auto sp = iter->second.wp.lock();
|
||||||
if(sp) {
|
if (sp) {
|
||||||
if(iter->second.flags != flags)
|
if (iter->second.flags != flags)
|
||||||
throw LMDBError("Can't open mdb with differing flags");
|
throw LMDBError("Can't open mdb with differing flags");
|
||||||
|
|
||||||
return sp;
|
return sp;
|
||||||
|
} else {
|
||||||
|
s_envs.erase(iter); // useful if make_shared fails
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
s_envs.erase(iter); // useful if make_shared fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, maxDBs);
|
auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, maxDBs);
|
||||||
s_envs[key] = {fresh, flags};
|
s_envs[key] = { fresh, flags };
|
||||||
|
|
||||||
return fresh;
|
return fresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MDBDbi MDBEnv::openDB(const string_view dbname, unsigned int flags)
|
MDBDbi MDBEnv::openDB(const string_view dbname, unsigned int flags)
|
||||||
{
|
{
|
||||||
unsigned int envflags;
|
unsigned int envflags;
|
||||||
mdb_env_get_flags(d_env, &envflags);
|
mdb_env_get_flags(d_env, &envflags);
|
||||||
/*
|
/*
|
||||||
This function must not be called from multiple concurrent transactions in the same process. A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
|
This function must not be called from multiple concurrent transactions in the same process. A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
|
||||||
*/
|
*/
|
||||||
std::lock_guard<std::mutex> l(d_openmut);
|
std::lock_guard<std::mutex> l(d_openmut);
|
||||||
|
|
||||||
if(!(envflags & MDB_RDONLY)) {
|
if (!(envflags & MDB_RDONLY)) {
|
||||||
auto rwt = getRWTransaction();
|
auto rwt = getRWTransaction();
|
||||||
MDBDbi ret = rwt->openDB(dbname, flags);
|
MDBDbi ret = rwt->openDB(dbname, flags);
|
||||||
rwt->commit();
|
rwt->commit();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBDbi ret;
|
||||||
|
{
|
||||||
|
auto rwt = getROTransaction();
|
||||||
|
ret = rwt->openDB(dbname, flags);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
MDBDbi ret;
|
|
||||||
{
|
|
||||||
auto rwt = getROTransaction();
|
|
||||||
ret = rwt->openDB(dbname, flags);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn):
|
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn)
|
||||||
MDBROTransactionImpl(parent, txn)
|
: MDBROTransactionImpl(parent, txn)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags)
|
MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags)
|
||||||
{
|
{
|
||||||
MDB_txn *result;
|
MDB_txn *result;
|
||||||
if(env->getRWTX())
|
if (env->getRWTX())
|
||||||
throw LMDBError("Duplicate RW transaction");
|
throw LMDBError("Duplicate RW transaction");
|
||||||
|
|
||||||
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
|
for (int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows
|
||||||
if(int rc=mdb_txn_begin(env->d_env, parent, flags, &result)) {
|
if (int rc = mdb_txn_begin(env->d_env, parent, flags, &result)) {
|
||||||
if(rc == MDB_MAP_RESIZED && tries < 2) {
|
if (rc == MDB_MAP_RESIZED && tries < 2) {
|
||||||
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
||||||
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
||||||
mdb_env_set_mapsize(env->d_env, 0);
|
mdb_env_set_mapsize(env->d_env, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw LMDBError("Unable to start RW transaction: ", rc);
|
throw LMDBError("Unable to start RW transaction: ", rc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
env->incRWTX();
|
||||||
}
|
return result;
|
||||||
env->incRWTX();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv* parent, unsigned int flags):
|
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, unsigned int flags)
|
||||||
MDBRWTransactionImpl(parent, openRWTransaction(parent, nullptr, flags))
|
: MDBRWTransactionImpl(parent, openRWTransaction(parent, nullptr, flags))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWTransactionImpl::~MDBRWTransactionImpl()
|
MDBRWTransactionImpl::~MDBRWTransactionImpl()
|
||||||
{
|
{
|
||||||
MDBRWTransactionImpl::abort();
|
MDBRWTransactionImpl::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBRWTransactionImpl::commit()
|
void MDBRWTransactionImpl::commit()
|
||||||
{
|
{
|
||||||
closeRORWCursors();
|
closeRORWCursors();
|
||||||
if (!d_txn) {
|
if (!d_txn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(const auto rc = mdb_txn_commit(d_txn)) {
|
if (const auto rc = mdb_txn_commit(d_txn)) {
|
||||||
throw LMDBError("Committing transaction: ", rc);
|
throw LMDBError("Committing transaction: ", rc);
|
||||||
}
|
}
|
||||||
environment().decRWTX();
|
environment().decRWTX();
|
||||||
d_txn = nullptr;
|
d_txn = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBRWTransactionImpl::abort()
|
void MDBRWTransactionImpl::abort()
|
||||||
{
|
{
|
||||||
closeRORWCursors();
|
closeRORWCursors();
|
||||||
if (!d_txn) {
|
if (!d_txn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mdb_txn_abort(d_txn);
|
mdb_txn_abort(d_txn);
|
||||||
// prevent the RO destructor from cleaning up the transaction itself
|
// prevent the RO destructor from cleaning up the transaction itself
|
||||||
environment().decRWTX();
|
environment().decRWTX();
|
||||||
d_txn = nullptr;
|
d_txn = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn):
|
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn)
|
||||||
d_parent(parent),
|
: d_parent(parent)
|
||||||
d_cursors(),
|
, d_cursors()
|
||||||
d_txn(txn)
|
, d_txn(txn)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MDB_txn *MDBROTransactionImpl::openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags)
|
MDB_txn *MDBROTransactionImpl::openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags)
|
||||||
{
|
{
|
||||||
if(env->getRWTX())
|
if (env->getRWTX())
|
||||||
throw LMDBError("Duplicate RO transaction");
|
throw LMDBError("Duplicate RO transaction");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */
|
A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */
|
||||||
MDB_txn *result = nullptr;
|
MDB_txn *result = nullptr;
|
||||||
for(int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows
|
for (int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows
|
||||||
if(const auto rc = mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) {
|
if (const auto rc = mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) {
|
||||||
if(rc == MDB_MAP_RESIZED && tries < 2) {
|
if (rc == MDB_MAP_RESIZED && tries < 2) {
|
||||||
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
||||||
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
||||||
mdb_env_set_mapsize(env->d_env, 0);
|
mdb_env_set_mapsize(env->d_env, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw LMDBError("Unable to start RO transaction: ", rc);
|
throw LMDBError("Unable to start RO transaction: ", rc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
env->incROTX();
|
||||||
}
|
|
||||||
env->incROTX();
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBROTransactionImpl::closeROCursors()
|
void MDBROTransactionImpl::closeROCursors()
|
||||||
{
|
{
|
||||||
// we need to move the vector away to ensure that the cursors don’t mess with our iteration.
|
// we need to move the vector away to ensure that the cursors don’t mess with our iteration.
|
||||||
std::vector<MDBROCursor*> buf;
|
std::vector<MDBROCursor *> buf;
|
||||||
std::swap(d_cursors, buf);
|
std::swap(d_cursors, buf);
|
||||||
for (auto &cursor: buf) {
|
for (auto &cursor : buf) {
|
||||||
cursor->close();
|
cursor->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, unsigned int flags):
|
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, unsigned int flags)
|
||||||
MDBROTransactionImpl(parent, openROTransaction(parent, nullptr, flags))
|
: MDBROTransactionImpl(parent, openROTransaction(parent, nullptr, flags))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransactionImpl::~MDBROTransactionImpl()
|
MDBROTransactionImpl::~MDBROTransactionImpl()
|
||||||
{
|
{
|
||||||
// this is safe because C++ will not call overrides of virtual methods in destructors.
|
// this is safe because C++ will not call overrides of virtual methods in destructors.
|
||||||
MDBROTransactionImpl::commit();
|
MDBROTransactionImpl::commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBROTransactionImpl::abort()
|
void MDBROTransactionImpl::abort()
|
||||||
{
|
{
|
||||||
closeROCursors();
|
closeROCursors();
|
||||||
// if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
|
// if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
|
||||||
if (d_txn) {
|
if (d_txn) {
|
||||||
d_parent->decROTX();
|
d_parent->decROTX();
|
||||||
mdb_txn_abort(d_txn); // this appears to work better than abort for r/o database opening
|
mdb_txn_abort(d_txn); // this appears to work better than abort for r/o database opening
|
||||||
d_txn = nullptr;
|
d_txn = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDBROTransactionImpl::commit()
|
void MDBROTransactionImpl::commit()
|
||||||
{
|
{
|
||||||
closeROCursors();
|
closeROCursors();
|
||||||
// if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
|
// if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
|
||||||
if (d_txn) {
|
if (d_txn) {
|
||||||
d_parent->decROTX();
|
d_parent->decROTX();
|
||||||
if (const auto rc = mdb_txn_commit(d_txn)) { // this appears to work better than abort for r/o database opening
|
if (const auto rc = mdb_txn_commit(d_txn)) { // this appears to work better than abort for r/o database opening
|
||||||
throw LMDBError("Error comitting transaction: ", rc);
|
throw LMDBError("Error comitting transaction: ", rc);
|
||||||
|
}
|
||||||
|
d_txn = nullptr;
|
||||||
}
|
}
|
||||||
d_txn = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MDBRWTransactionImpl::clear(MDB_dbi dbi)
|
void MDBRWTransactionImpl::clear(MDB_dbi dbi)
|
||||||
{
|
{
|
||||||
if(const auto rc = mdb_drop(d_txn, dbi, 0)) {
|
if (const auto rc = mdb_drop(d_txn, dbi, 0)) {
|
||||||
throw LMDBError("Error clearing database: ", rc);
|
throw LMDBError("Error clearing database: ", rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi& dbi)
|
MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi &dbi)
|
||||||
{
|
{
|
||||||
MDB_cursor *cursor;;
|
MDB_cursor *cursor;
|
||||||
if(const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) {
|
;
|
||||||
throw LMDBError("Error creating RO cursor: ", rc);
|
if (const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) {
|
||||||
}
|
throw LMDBError("Error creating RO cursor: ", rc);
|
||||||
return MDBRWCursor(d_rw_cursors, cursor);
|
}
|
||||||
|
return MDBRWCursor(d_rw_cursors, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWCursor MDBRWTransactionImpl::getCursor(const MDBDbi &dbi)
|
MDBRWCursor MDBRWTransactionImpl::getCursor(const MDBDbi &dbi)
|
||||||
{
|
{
|
||||||
return getRWCursor(dbi);
|
return getRWCursor(dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWTransaction MDBRWTransactionImpl::getRWTransaction()
|
MDBRWTransaction MDBRWTransactionImpl::getRWTransaction()
|
||||||
{
|
{
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
if (const auto rc = mdb_txn_begin(environment(), *this, 0, &txn)) {
|
if (const auto rc = mdb_txn_begin(environment(), *this, 0, &txn)) {
|
||||||
throw LMDBError("Failed to start child transaction: ", rc);
|
throw LMDBError("Failed to start child transaction: ", rc);
|
||||||
}
|
}
|
||||||
// we need to increase the counter here because commit/abort on the child transaction will decrease it
|
// we need to increase the counter here because commit/abort on the child transaction will decrease it
|
||||||
environment().incRWTX();
|
environment().incRWTX();
|
||||||
return MDBRWTransaction(new MDBRWTransactionImpl(&environment(), txn));
|
return MDBRWTransaction(new MDBRWTransactionImpl(&environment(), txn));
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransaction MDBRWTransactionImpl::getROTransaction()
|
MDBROTransaction MDBRWTransactionImpl::getROTransaction()
|
||||||
{
|
{
|
||||||
return getRWTransaction();
|
return getRWTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransaction MDBEnv::getROTransaction()
|
MDBROTransaction MDBEnv::getROTransaction()
|
||||||
{
|
{
|
||||||
return MDBROTransaction(new MDBROTransactionImpl(this));
|
return MDBROTransaction(new MDBROTransactionImpl(this));
|
||||||
}
|
}
|
||||||
MDBRWTransaction MDBEnv::getRWTransaction()
|
MDBRWTransaction MDBEnv::getRWTransaction()
|
||||||
{
|
{
|
||||||
return MDBRWTransaction(new MDBRWTransactionImpl(this));
|
return MDBRWTransaction(new MDBRWTransactionImpl(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MDBRWTransactionImpl::closeRWCursors()
|
void MDBRWTransactionImpl::closeRWCursors()
|
||||||
{
|
{
|
||||||
decltype(d_rw_cursors) buf;
|
decltype(d_rw_cursors) buf;
|
||||||
std::swap(d_rw_cursors, buf);
|
std::swap(d_rw_cursors, buf);
|
||||||
for (auto &cursor: buf) {
|
for (auto &cursor : buf) {
|
||||||
cursor->close();
|
cursor->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi& dbi)
|
MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi &dbi)
|
||||||
{
|
{
|
||||||
return getROCursor(dbi);
|
return getROCursor(dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROCursor MDBROTransactionImpl::getROCursor(const MDBDbi &dbi)
|
MDBROCursor MDBROTransactionImpl::getROCursor(const MDBDbi &dbi)
|
||||||
{
|
{
|
||||||
MDB_cursor *cursor;
|
MDB_cursor *cursor;
|
||||||
if(const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) {
|
if (const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) {
|
||||||
throw LMDBError("Error creating RO cursor: ", rc);
|
throw LMDBError("Error creating RO cursor: ", rc);
|
||||||
}
|
}
|
||||||
return MDBROCursor(d_cursors, cursor);
|
return MDBROCursor(d_cursors, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace LMDBSafe
|
||||||
|
|
882
lmdb-safe.hh
882
lmdb-safe.hh
File diff suppressed because it is too large
Load Diff
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
namespace LMDBSafe {
|
namespace LMDBSafe {
|
||||||
|
|
||||||
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi)
|
unsigned int MDBGetMaxID(MDBRWTransaction &txn, MDBDbi &dbi)
|
||||||
{
|
{
|
||||||
auto cursor = txn->getRWCursor(dbi);
|
auto cursor = txn->getRWCursor(dbi);
|
||||||
MDBOutVal maxidval, maxcontent;
|
MDBOutVal maxidval, maxcontent;
|
||||||
unsigned int maxid{0};
|
unsigned int maxid{ 0 };
|
||||||
if(!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
if (!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
||||||
maxid = maxidval.get<unsigned int>();
|
maxid = maxidval.get<unsigned int>();
|
||||||
}
|
}
|
||||||
return maxid;
|
return maxid;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace LMDBSafe
|
||||||
|
|
1146
lmdb-typed.hh
1146
lmdb-typed.hh
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue