diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1951,7 +1951,7 @@ nMempoolSizeMax * (1.0 / 1024 / 1024)); bool fLoaded = false; - while (!fLoaded) { + while (!fLoaded && !fRequestShutdown) { bool fReset = fReindex; std::string strLoadError; @@ -1984,6 +1984,7 @@ strLoadError = _("Error upgrading chainstate database"); break; } + if (fRequestShutdown) break; if (!LoadBlockIndex(chainparams)) { strLoadError = _("Error loading block database"); @@ -2076,7 +2077,7 @@ fLoaded = true; } while (false); - if (!fLoaded) { + if (!fLoaded && !fRequestShutdown) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -634,6 +634,7 @@ // http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType("Amount"); + qRegisterMetaType>("std::function"); // Need to register any types Qt doesn't know about if you intend // to use them with the signal/slot mechanism Qt provides. Even pointers. diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_SPLASHSCREEN_H #include +#include class CWallet; class NetworkStyle; @@ -35,6 +36,12 @@ void showMessage(const QString &message, int alignment, const QColor &color); + /** Sets the break action */ + void setBreakAction(const std::function &action); + +protected: + bool eventFilter(QObject *obj, QEvent *ev); + private: /** Connect core signals to splash screen */ void subscribeToCoreSignals(); @@ -49,6 +56,8 @@ int curAlignment; QList connectedWallets; + + std::function breakAction; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -146,12 +146,23 @@ move(QApplication::desktop()->screenGeometry().center() - r.center()); subscribeToCoreSignals(); + installEventFilter(this); } SplashScreen::~SplashScreen() { unsubscribeFromCoreSignals(); } +bool SplashScreen::eventFilter(QObject *obj, QEvent *ev) { + if (ev->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(ev); + if (keyEvent->text()[0] == 'q' && breakAction != nullptr) { + breakAction(); + } + } + return QObject::eventFilter(obj, ev); +} + void SplashScreen::slotFinish(QWidget *mainWin) { Q_UNUSED(mainWin); @@ -174,6 +185,16 @@ InitMessage(splash, title + strprintf("%d", nProgress) + "%"); } +void SplashScreen::setBreakAction(const std::function &action) { + breakAction = action; +} + +static void SetProgressBreakAction(SplashScreen *splash, + const std::function &action) { + QMetaObject::invokeMethod(splash, "setBreakAction", Qt::QueuedConnection, + Q_ARG(std::function, action)); +} + #ifdef ENABLE_WALLET void SplashScreen::ConnectWallet(CWallet *wallet) { wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); @@ -185,6 +206,8 @@ // Connect signals to client uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + uiInterface.SetProgressBreakAction.connect( + boost::bind(SetProgressBreakAction, this, _1)); #ifdef ENABLE_WALLET uiInterface.LoadWallet.connect( boost::bind(&SplashScreen::ConnectWallet, this, _1)); diff --git a/src/txdb.cpp b/src/txdb.cpp --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -8,7 +8,9 @@ #include "chainparams.h" #include "config.h" #include "hash.h" +#include "init.h" #include "pow.h" +#include "ui_interface.h" #include "uint256.h" #include @@ -334,16 +336,39 @@ return true; } - LogPrintf("Upgrading database...\n"); + int64_t count = 0; + LogPrintf("Upgrading utxo-set database...\n"); + LogPrintf("[0%%]..."); size_t batch_size = 1 << 24; CDBBatch batch(db); + uiInterface.SetProgressBreakAction(StartShutdown); + int reportDone = 0; while (pcursor->Valid()) { boost::this_thread::interruption_point(); + if (ShutdownRequested()) { + break; + } + std::pair key; if (!pcursor->GetKey(key) || key.first != DB_COINS) { break; } + if (count++ % 256 == 0) { + uint32_t high = + 0x100 * *key.second.begin() + *(key.second.begin() + 1); + int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5); + uiInterface.ShowProgress( + _("Upgrading UTXO database") + "\n" + + _("(press q to shutdown and continue later)") + "\n", + percentageDone); + if (reportDone < percentageDone / 10) { + // report max. every 10% step + LogPrintf("[%d%%]...", percentageDone); + reportDone = percentageDone / 10; + } + } + CCoins old_coins; if (!pcursor->GetValue(old_coins)) { return error("%s: cannot parse CCoins record", __func__); @@ -371,5 +396,7 @@ } db.WriteBatch(batch); - return true; + uiInterface.SetProgressBreakAction(std::function()); + LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); + return !ShutdownRequested(); } diff --git a/src/ui_interface.h b/src/ui_interface.h --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -115,6 +115,11 @@ boost::signals2::signal ShowProgress; + /** Set progress break action (possible "cancel button" triggers that + * action) */ + boost::signals2::signal action)> + SetProgressBreakAction; + /** New block has been accepted */ boost::signals2::signal NotifyBlockTip;