Replace generic CScopedDBTransaction with specialized CEvoDBScopedCommitter (#3292)

This has the wanted side effect of proper locking of "cs" inside
CommitCurTransaction and RollbackCurTransaction, which was not easily
possible to implement in the generic version. This fixes some rare crashes.
This commit is contained in:
Alexander Block 2020-01-17 16:01:22 +01:00
parent 8fd486c6bc
commit 1d9adbe639
3 changed files with 61 additions and 49 deletions

View File

@ -724,49 +724,4 @@ public:
}
};
template<typename Parent, typename CommitTarget>
class CScopedDBTransaction {
public:
typedef CDBTransaction<Parent, CommitTarget> Transaction;
private:
Transaction &dbTransaction;
std::function<void ()> commitHandler;
std::function<void ()> rollbackHandler;
bool didCommitOrRollback{};
public:
CScopedDBTransaction(Transaction &dbTx) : dbTransaction(dbTx) {}
~CScopedDBTransaction() {
if (!didCommitOrRollback)
Rollback();
}
void Commit() {
assert(!didCommitOrRollback);
didCommitOrRollback = true;
dbTransaction.Commit();
if (commitHandler)
commitHandler();
}
void Rollback() {
assert(!didCommitOrRollback);
didCommitOrRollback = true;
dbTransaction.Clear();
if (rollbackHandler)
rollbackHandler();
}
static std::unique_ptr<CScopedDBTransaction<Parent, CommitTarget>> Begin(Transaction &dbTx) {
assert(dbTx.IsClean());
return std::make_unique<CScopedDBTransaction<Parent, CommitTarget>>(dbTx);
}
void SetCommitHandler(const std::function<void ()> &h) {
commitHandler = h;
}
void SetRollbackHandler(const std::function<void ()> &h) {
rollbackHandler = h;
}
};
#endif // BITCOIN_DBWRAPPER_H

View File

@ -6,6 +6,31 @@
CEvoDB* evoDb;
CEvoDBScopedCommitter::CEvoDBScopedCommitter(CEvoDB &_evoDB) :
evoDB(_evoDB)
{
}
CEvoDBScopedCommitter::~CEvoDBScopedCommitter()
{
if (!didCommitOrRollback)
Rollback();
}
void CEvoDBScopedCommitter::Commit()
{
assert(!didCommitOrRollback);
didCommitOrRollback = true;
evoDB.CommitCurTransaction();
}
void CEvoDBScopedCommitter::Rollback()
{
assert(!didCommitOrRollback);
didCommitOrRollback = true;
evoDB.RollbackCurTransaction();
}
CEvoDB::CEvoDB(size_t nCacheSize, bool fMemory, bool fWipe) :
db(fMemory ? "" : (GetDataDir() / "evodb"), nCacheSize, fMemory, fWipe),
rootBatch(db),
@ -14,6 +39,18 @@ CEvoDB::CEvoDB(size_t nCacheSize, bool fMemory, bool fWipe) :
{
}
void CEvoDB::CommitCurTransaction()
{
LOCK(cs);
curDBTransaction.Commit();
}
void CEvoDB::RollbackCurTransaction()
{
LOCK(cs);
curDBTransaction.Clear();
}
bool CEvoDB::CommitRootTransaction()
{
assert(curDBTransaction.IsClean());

View File

@ -13,6 +13,22 @@
// "b_b2" was used after compact diffs were introduced
static const std::string EVODB_BEST_BLOCK = "b_b2";
class CEvoDB;
class CEvoDBScopedCommitter
{
private:
CEvoDB& evoDB;
bool didCommitOrRollback{false};
public:
explicit CEvoDBScopedCommitter(CEvoDB& _evoDB);
~CEvoDBScopedCommitter();
void Commit();
void Rollback();
};
class CEvoDB
{
private:
@ -21,7 +37,6 @@ private:
typedef CDBTransaction<CDBWrapper, CDBBatch> RootTransaction;
typedef CDBTransaction<RootTransaction, RootTransaction> CurTransaction;
typedef CScopedDBTransaction<RootTransaction, RootTransaction> ScopedTransaction;
CDBBatch rootBatch;
RootTransaction rootDBTransaction;
@ -30,11 +45,10 @@ private:
public:
CEvoDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
std::unique_ptr<ScopedTransaction> BeginTransaction()
std::unique_ptr<CEvoDBScopedCommitter> BeginTransaction()
{
LOCK(cs);
auto t = ScopedTransaction::Begin(curDBTransaction);
return t;
return std::make_unique<CEvoDBScopedCommitter>(*this);
}
CurTransaction& GetCurTransaction()
@ -84,6 +98,12 @@ public:
bool VerifyBestBlock(const uint256& hash);
void WriteBestBlock(const uint256& hash);
private:
// only CEvoDBScopedCommitter is allowed to invoke these
friend class CEvoDBScopedCommitter;
void CommitCurTransaction();
void RollbackCurTransaction();
};
extern CEvoDB* evoDb;