diff --git a/src/coins.h b/src/coins.h --- a/src/coins.h +++ b/src/coins.h @@ -14,6 +14,7 @@ #include #include +#include #include /** @@ -311,4 +312,30 @@ //! lookups to database, so it should be used with care. const Coin &AccessByTxid(const CCoinsViewCache &cache, const TxId &txid); +/** + * This is a minimally invasive approach to shutdown on LevelDB read errors from + * the chainstate, while keeping user interface out of the common library, which + * is shared between bitcoind, and bitcoin-qt and non-server tools. + * + * Writes do not need similar protection, as failure to write is handled by the + * caller. + */ +class CCoinsViewErrorCatcher final : public CCoinsViewBacked { +public: + explicit CCoinsViewErrorCatcher(CCoinsView *view) + : CCoinsViewBacked(view) {} + + void AddReadErrCallback(std::function f) { + m_err_callbacks.emplace_back(std::move(f)); + } + + bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; + +private: + /** + * A list of callbacks to execute upon leveldb read error. + */ + std::vector> m_err_callbacks; +}; + #endif // BITCOIN_COINS_H diff --git a/src/coins.cpp b/src/coins.cpp --- a/src/coins.cpp +++ b/src/coins.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -325,3 +326,21 @@ return coinEmpty; } + +bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, + Coin &coin) const { + try { + return CCoinsViewBacked::GetCoin(outpoint, coin); + } catch (const std::runtime_error &e) { + for (auto f : m_err_callbacks) { + f(); + } + LogPrintf("Error reading from database: %s\n", e.what()); + // Starting the shutdown sequence and returning false to the caller + // would be interpreted as 'entry not found' (as opposed to unable to + // read data), and could lead to invalid interpretation. Just exit + // immediately, as we can't continue anyway, and all writes should be + // atomic. + std::abort(); + } +} diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -144,35 +145,6 @@ // ShutdownRequested() getting set, and then does the normal Qt shutdown thing. // -/** - * This is a minimally invasive approach to shutdown on LevelDB read errors from - * the chainstate, while keeping user interface out of the common library, which - * is shared between bitcoind, and bitcoin-qt and non-server tools. - */ -class CCoinsViewErrorCatcher final : public CCoinsViewBacked { -public: - explicit CCoinsViewErrorCatcher(CCoinsView *view) - : CCoinsViewBacked(view) {} - bool GetCoin(const COutPoint &outpoint, Coin &coin) const override { - try { - return CCoinsViewBacked::GetCoin(outpoint, coin); - } catch (const std::runtime_error &e) { - uiInterface.ThreadSafeMessageBox( - _("Error reading from database, shutting down.").translated, "", - CClientUIInterface::MSG_ERROR); - LogPrintf("Error reading from database: %s\n", e.what()); - // Starting the shutdown sequence and returning false to the caller - // would be interpreted as 'entry not found' (as opposed to unable - // to read data), and could lead to invalid interpretation. Just - // exit immediately, as we can't continue anyway, and all writes - // should be atomic. - abort(); - } - } - // Writes do not need similar protection, as failure to write is handled by - // the caller. -}; - static std::unique_ptr pcoinscatcher; static std::unique_ptr globalVerifyHandle; @@ -2500,6 +2472,12 @@ nCoinDBCache, false, fReset || fReindexChainState)); pcoinscatcher.reset( new CCoinsViewErrorCatcher(pcoinsdbview.get())); + pcoinscatcher->AddReadErrCallback([]() { + uiInterface.ThreadSafeMessageBox( + _("Error reading from database, shutting down.") + .translated, + "", CClientUIInterface::MSG_ERROR); + }); // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex