diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -237,7 +237,7 @@ explicit BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode = "r+", bool fFlushOnCloseIn = true); - ~BerkeleyBatch() override { Close(); } + ~BerkeleyBatch() override; BerkeleyBatch(const BerkeleyBatch &) = delete; BerkeleyBatch &operator=(const BerkeleyBatch &) = delete; diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -341,14 +341,26 @@ bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), m_database(database) { + database.AddRef(); + database.Open(pszMode); fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); fFlushOnClose = fFlushOnCloseIn; env = database.env.get(); - if (database.IsDummy()) { - return; + pdb = database.m_db.get(); + strFile = database.strFile; + bool fCreate = strchr(pszMode, 'c') != nullptr; + if (fCreate && !Exists(std::string("version"))) { + bool fTmp = fReadOnly; + fReadOnly = false; + Write(std::string("version"), CLIENT_VERSION); + fReadOnly = fTmp; } +} - const std::string &strFilename = database.strFile; +void BerkeleyDatabase::Open(const char *pszMode) { + if (IsDummy()) { + return; + } bool fCreate = strchr(pszMode, 'c') != nullptr; unsigned int nFlags = DB_THREAD; @@ -361,10 +373,10 @@ bilingual_str open_err; if (!env->Open(open_err)) { throw std::runtime_error( - "BerkeleyBatch: Failed to open database environment."); + "BerkeleyDatabase: Failed to open database environment."); } - pdb = database.m_db.get(); - if (pdb == nullptr) { + + if (m_db == nullptr) { int ret; std::unique_ptr pdb_temp = std::make_unique(env->dbenv.get(), 0); @@ -374,67 +386,53 @@ DbMpoolFile *mpf = pdb_temp->get_mpf(); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); if (ret != 0) { - throw std::runtime_error( - strprintf("BerkeleyBatch: Failed to configure for no " - "temp file backing for database %s", - strFilename)); + throw std::runtime_error(strprintf( + "BerkeleyDatabase: Failed to configure for no " + "temp file backing for database %s", + strFile)); } } ret = pdb_temp->open( - nullptr, // Txn pointer - fMockDb ? nullptr : strFilename.c_str(), // Filename - fMockDb ? strFilename.c_str() : "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags + nullptr, // Txn pointer + fMockDb ? nullptr : strFile.c_str(), // Filename + fMockDb ? strFile.c_str() : "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags 0); if (ret != 0) { - throw std::runtime_error( - strprintf("BerkeleyBatch: Error %d, can't open database %s", - ret, strFilename)); + throw std::runtime_error(strprintf( + "BerkeleyDatabase: Error %d, can't open database %s", ret, + strFile)); } // Call CheckUniqueFileid on the containing BDB environment to - // avoid BDB data consistency bugs that happen when different data - // files in the same environment have the same fileid. + // avoid BDB data consistency bugs that happen when different + // data files in the same environment have the same fileid. // - // Also call CheckUniqueFileid on all the other g_dbenvs to prevent - // bitcoin from opening the same data file through another - // environment when the file is referenced through equivalent but - // not obviously identical symlinked or hard linked or bind mounted - // paths. In the future a more relaxed check for equal inode and - // device ids could be done instead, which would allow opening - // different backup copies of a wallet at the same time. Maybe even - // more ideally, an exclusive lock for accessing the database could - // be implemented, so no equality checks are needed at all. (Newer - // versions of BDB have an set_lk_exclusive method for this - // purpose, but the older version we use does not.) + // Also call CheckUniqueFileid on all the other g_dbenvs to + // prevent bitcoin from opening the same data file through + // another environment when the file is referenced through + // equivalent but not obviously identical symlinked or hard + // linked or bind mounted paths. In the future a more relaxed + // check for equal inode and device ids could be done instead, + // which would allow opening different backup copies of a wallet + // at the same time. Maybe even more ideally, an exclusive lock + // for accessing the database could be implemented, so no + // equality checks are needed at all. (Newer versions of BDB + // have an set_lk_exclusive method for this purpose, but the + // older version we use does not.) for (const auto &dbenv : g_dbenvs) { - CheckUniqueFileid(*dbenv.second.lock().get(), strFilename, - *pdb_temp, this->env->m_fileids[strFilename]); + CheckUniqueFileid(*dbenv.second.lock().get(), strFile, + *pdb_temp, this->env->m_fileids[strFile]); } - pdb = pdb_temp.release(); - database.m_db.reset(pdb); - - if (fCreate && !Exists(std::string("version"))) { - bool fTmp = fReadOnly; - fReadOnly = false; - Write(std::string("version"), CLIENT_VERSION); - fReadOnly = fTmp; - } + m_db.reset(pdb_temp.release()); } - database.AddRef(); - strFile = strFilename; } } -void BerkeleyDatabase::Open(const char *mode) { - throw std::logic_error("BerkeleyDatabase does not implement Open. " - "This function should not be called."); -} - void BerkeleyBatch::Flush() { if (activeTxn) { return; @@ -446,8 +444,8 @@ nMinutes = 1; } - // env is nullptr for dummy databases (i.e. in tests). Don't actually flush - // if env is nullptr so we don't segfault + // env is nullptr for dummy databases (i.e. in tests). Don't actually + // flush if env is nullptr so we don't segfault if (env) { env->dbenv->txn_checkpoint( nMinutes @@ -461,6 +459,11 @@ ++nUpdateCounter; } +BerkeleyBatch::~BerkeleyBatch() { + Close(); + m_database.RemoveRef(); +} + void BerkeleyBatch::Close() { if (!pdb) { return; @@ -475,8 +478,6 @@ if (fFlushOnClose) { Flush(); } - - m_database.RemoveRef(); } void BerkeleyEnvironment::CloseDb(const std::string &strFile) { @@ -624,7 +625,8 @@ void BerkeleyEnvironment::Flush(bool fShutdown) { int64_t nStart = GetTimeMillis(); - // Flush log data to the actual data file on all files that are not in use + // Flush log data to the actual data file on all files that are not in + // use LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); @@ -640,10 +642,10 @@ if (nRefCount < 0) { continue; } - LogPrint( - BCLog::WALLETDB, - "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", - strFile, nRefCount); + LogPrint(BCLog::WALLETDB, + "BerkeleyEnvironment::Flush: Flushing %s (refcount = " + "%d)...\n", + strFile, nRefCount); if (nRefCount == 0) { // Move log data to the dat file CloseDb(strFile); @@ -927,7 +929,9 @@ void BerkeleyDatabase::RemoveRef() { LOCK(cs_db); m_refcount--; - env->m_db_in_use.notify_all(); + if (env) { + env->m_db_in_use.notify_all(); + } } std::unique_ptr