Skip to content

Commit 81e7748

Browse files
committed
Merge #336: Do not exit and re-enter main event loop during shutdown
451ca24 qt, refactor: Drop intermediate BitcoinApplication::shutdownResult slot (Hennadii Stepanov) f3a17bb qt: Do not exit and re-enter main event loop during shutdown (Hennadii Stepanov) b4e0d2c qt, refactor: Allocate SendConfirmationDialog instances on heap (Hennadii Stepanov) 332dea2 qt, refactor: Keep HelpMessageDialog in the main event loop (Hennadii Stepanov) c8bae37 qt, refactor: Keep PSBTOperationsDialog in the main event loop (Hennadii Stepanov) 7fa91e8 qt, refactor: Keep AskPassphraseDialog in the main event loop (Hennadii Stepanov) 6f6fde3 qt, refactor: Keep EditAddressDialog in the main event loop (Hennadii Stepanov) 59f7ba4 qt, refactor: Keep CoinControlDialog in the main event loop (Hennadii Stepanov) 7830cd0 qt, refactor: Keep OptionsDialog in the main event loop (Hennadii Stepanov) 13f6188 qt: Add GUIUtil::ShowModalDialogAndDeleteOnClose (Hennadii Stepanov) Pull request description: On master (1ef34ee) during shutdown `QApplication` exits the main event loop, then re-enter again. This PR streamlines shutdown process by removing the need to interrupt the main event loop, that is required for #59. Also, blocking [`QDialog::exec()`](https://doc.qt.io/qt-5/qdialog.html#exec) calls are replaced with safer [`QDialog::show()`](https://doc.qt.io/qt-5/qwidget.html#show), except for `SendConfirmationDialog` as that change is not trivial (marked as TODO). The [`QDialog::open()`](https://doc.qt.io/qt-5/qdialog.html#open) was not used because the actual modality mode (application modal or window modal) of a dialog depends on whether it has a parent. This PR does not change behavior, and all touched dialogs are still application modal. As a follow up, a design research could suggest to make some dialogs window modal. NOTE for reviewers: quitting app while a dialog is open (e.g., via systray icon menu) must work fine. ACKs for top commit: laanwj: Code review and lighly tested ACK 451ca24 promag: ACK 451ca24, just changed signal to `quitRequested`. Tree-SHA512: ef01ab6ed803b202e776019a4e1f592e816f7bc786e00574b25a0bf16be2374ddf9db21f0a26da08700df7ef0ab9e879550df46dcfe3b6d940f5ed02ca5f8447
2 parents f529290 + 451ca24 commit 81e7748

14 files changed

+75
-60
lines changed

src/qt/addressbookpage.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,14 @@ void AddressBookPage::onEditAction()
182182
if(indexes.isEmpty())
183183
return;
184184

185-
EditAddressDialog dlg(
185+
auto dlg = new EditAddressDialog(
186186
tab == SendingTab ?
187187
EditAddressDialog::EditSendingAddress :
188188
EditAddressDialog::EditReceivingAddress, this);
189-
dlg.setModel(model);
189+
dlg->setModel(model);
190190
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
191-
dlg.loadRow(origIndex.row());
192-
dlg.exec();
191+
dlg->loadRow(origIndex.row());
192+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
193193
}
194194

195195
void AddressBookPage::on_newAddress_clicked()

src/qt/bitcoin.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <QThread>
5656
#include <QTimer>
5757
#include <QTranslator>
58+
#include <QWindow>
5859

5960
#if defined(QT_STATICPLUGIN)
6061
#include <QtPlugin>
@@ -259,6 +260,7 @@ void BitcoinApplication::createOptionsModel(bool resetSettings)
259260
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
260261
{
261262
window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
263+
connect(window, &BitcoinGUI::quitRequested, this, &BitcoinApplication::requestShutdown);
262264

263265
pollShutdownTimer = new QTimer(window);
264266
connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
@@ -296,7 +298,7 @@ void BitcoinApplication::startThread()
296298

297299
/* communication to and from thread */
298300
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
299-
connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &BitcoinApplication::shutdownResult);
301+
connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit);
300302
connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
301303
connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
302304
connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
@@ -326,13 +328,17 @@ void BitcoinApplication::requestInitialize()
326328

327329
void BitcoinApplication::requestShutdown()
328330
{
331+
for (const auto w : QGuiApplication::topLevelWindows()) {
332+
w->hide();
333+
}
334+
329335
// Show a simple window indicating shutdown status
330336
// Do this first as some of the steps may take some time below,
331337
// for example the RPC console may still be executing a command.
332338
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
333339

334340
qDebug() << __func__ << ": Requesting shutdown";
335-
window->hide();
341+
336342
// Must disconnect node signals otherwise current thread can deadlock since
337343
// no event loop is running.
338344
window->unsubscribeFromCoreSignals();
@@ -409,15 +415,10 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
409415
pollShutdownTimer->start(200);
410416
} else {
411417
Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
412-
quit(); // Exit first main loop invocation
418+
requestShutdown();
413419
}
414420
}
415421

416-
void BitcoinApplication::shutdownResult()
417-
{
418-
quit(); // Exit second main loop invocation after shutdown finished
419-
}
420-
421422
void BitcoinApplication::handleRunawayException(const QString &message)
422423
{
423424
QMessageBox::critical(
@@ -639,8 +640,6 @@ int GuiMain(int argc, char* argv[])
639640
#if defined(Q_OS_WIN)
640641
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
641642
#endif
642-
app.exec();
643-
app.requestShutdown();
644643
app.exec();
645644
rv = app.getReturnValue();
646645
} else {

src/qt/bitcoin.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ class BitcoinApplication: public QApplication
6161

6262
/// Request core initialization
6363
void requestInitialize();
64-
/// Request core shutdown
65-
void requestShutdown();
6664

6765
/// Get process return value
6866
int getReturnValue() const { return returnValue; }
@@ -77,7 +75,8 @@ class BitcoinApplication: public QApplication
7775

7876
public Q_SLOTS:
7977
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
80-
void shutdownResult();
78+
/// Request core shutdown
79+
void requestShutdown();
8180
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
8281
void handleRunawayException(const QString &message);
8382

src/qt/bitcoingui.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ void BitcoinGUI::createActions()
373373
m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
374374
m_mask_values_action->setCheckable(true);
375375

376-
connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
376+
connect(quitAction, &QAction::triggered, this, &BitcoinGUI::quitRequested);
377377
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
378378
connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
379379
connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked);
@@ -850,8 +850,8 @@ void BitcoinGUI::aboutClicked()
850850
if(!clientModel)
851851
return;
852852

853-
HelpMessageDialog dlg(this, true);
854-
dlg.exec();
853+
auto dlg = new HelpMessageDialog(this, /* about */ true);
854+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
855855
}
856856

857857
void BitcoinGUI::showDebugWindow()
@@ -992,10 +992,11 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
992992
if (!clientModel || !clientModel->getOptionsModel())
993993
return;
994994

995-
OptionsDialog dlg(this, enableWallet);
996-
dlg.setCurrentTab(tab);
997-
dlg.setModel(clientModel->getOptionsModel());
998-
dlg.exec();
995+
auto dlg = new OptionsDialog(this, enableWallet);
996+
connect(dlg, &OptionsDialog::quitOnReset, this, &BitcoinGUI::quitRequested);
997+
dlg->setCurrentTab(tab);
998+
dlg->setModel(clientModel->getOptionsModel());
999+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
9991000
}
10001001

10011002
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
@@ -1218,7 +1219,7 @@ void BitcoinGUI::closeEvent(QCloseEvent *event)
12181219
// close rpcConsole in case it was open to make some space for the shutdown window
12191220
rpcConsole->close();
12201221

1221-
QApplication::quit();
1222+
Q_EMIT quitRequested();
12221223
}
12231224
else
12241225
{
@@ -1412,7 +1413,7 @@ void BitcoinGUI::detectShutdown()
14121413
{
14131414
if(rpcConsole)
14141415
rpcConsole->hide();
1415-
qApp->quit();
1416+
Q_EMIT quitRequested();
14161417
}
14171418
}
14181419

src/qt/bitcoingui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ class BitcoinGUI : public QMainWindow
214214
void openOptionsDialogWithTab(OptionsDialog::Tab tab);
215215

216216
Q_SIGNALS:
217+
void quitRequested();
217218
/** Signal raised when a URI was entered or dragged to the GUI */
218219
void receivedURI(const QString &uri);
219220
/** Signal raised when RPC console shown */

src/qt/guiutil.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <QClipboard>
3737
#include <QDateTime>
3838
#include <QDesktopServices>
39+
#include <QDialog>
3940
#include <QDoubleValidator>
4041
#include <QFileDialog>
4142
#include <QFont>
@@ -970,4 +971,11 @@ void PrintSlotException(
970971
PrintExceptionContinue(exception, description.c_str());
971972
}
972973

974+
void ShowModalDialogAndDeleteOnClose(QDialog* dialog)
975+
{
976+
dialog->setAttribute(Qt::WA_DeleteOnClose);
977+
dialog->setWindowModality(Qt::ApplicationModal);
978+
dialog->show();
979+
}
980+
973981
} // namespace GUIUtil

src/qt/guiutil.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QAbstractButton;
4141
class QAbstractItemView;
4242
class QAction;
4343
class QDateTime;
44+
class QDialog;
4445
class QFont;
4546
class QKeySequence;
4647
class QLineEdit;
@@ -417,6 +418,11 @@ namespace GUIUtil
417418
type);
418419
}
419420

421+
/**
422+
* Shows a QDialog instance asynchronously, and deletes it on close.
423+
*/
424+
void ShowModalDialogAndDeleteOnClose(QDialog* dialog);
425+
420426
} // namespace GUIUtil
421427

422428
#endif // BITCOIN_QT_GUIUTIL_H

src/qt/optionsdialog.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ void OptionsDialog::on_resetButton_clicked()
292292

293293
/* reset all options and close GUI */
294294
model->Reset();
295-
QApplication::quit();
295+
close();
296+
Q_EMIT quitOnReset();
296297
}
297298
}
298299

src/qt/optionsdialog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ private Q_SLOTS:
6868

6969
Q_SIGNALS:
7070
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);
71+
void quitOnReset();
7172

7273
private:
7374
Ui::OptionsDialog *ui;

src/qt/sendcoinsdialog.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,10 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
399399

400400
const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
401401
const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
402-
SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
403-
confirmationDialog.exec();
404-
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
402+
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
403+
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
404+
// TODO: Replace QDialog::exec() with safer QDialog::show().
405+
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
405406

406407
if(retval != QMessageBox::Yes)
407408
{
@@ -914,9 +915,9 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
914915
// Coin Control: button inputs -> show actual coin control dialog
915916
void SendCoinsDialog::coinControlButtonClicked()
916917
{
917-
CoinControlDialog dlg(*m_coin_control, model, platformStyle);
918-
dlg.exec();
919-
coinControlUpdateLabels();
918+
auto dlg = new CoinControlDialog(*m_coin_control, model, platformStyle);
919+
connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
920+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
920921
}
921922

922923
// Coin Control: checkbox custom change address

src/qt/transactionview.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -504,22 +504,22 @@ void TransactionView::editLabel()
504504
// Determine type of address, launch appropriate editor dialog type
505505
QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
506506

507-
EditAddressDialog dlg(
507+
auto dlg = new EditAddressDialog(
508508
type == AddressTableModel::Receive
509509
? EditAddressDialog::EditReceivingAddress
510510
: EditAddressDialog::EditSendingAddress, this);
511-
dlg.setModel(addressBook);
512-
dlg.loadRow(idx);
513-
dlg.exec();
511+
dlg->setModel(addressBook);
512+
dlg->loadRow(idx);
513+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
514514
}
515515
else
516516
{
517517
// Add sending address
518-
EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
518+
auto dlg = new EditAddressDialog(EditAddressDialog::NewSendingAddress,
519519
this);
520-
dlg.setModel(addressBook);
521-
dlg.setAddress(address);
522-
dlg.exec();
520+
dlg->setModel(addressBook);
521+
dlg->setAddress(address);
522+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
523523
}
524524
}
525525
}

src/qt/walletframe.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,9 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
221221
return;
222222
}
223223

224-
PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
224+
auto dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
225225
dlg->openWithPSBT(psbtx);
226-
dlg->setAttribute(Qt::WA_DeleteOnClose);
227-
dlg->exec();
226+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
228227
}
229228

230229
void WalletFrame::encryptWallet()

src/qt/walletmodel.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,10 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
506506
questionString.append(tr("Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy."));
507507
}
508508

509-
SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
510-
confirmationDialog.exec();
511-
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
509+
auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString);
510+
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
511+
// TODO: Replace QDialog::exec() with safer QDialog::show().
512+
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
512513

513514
// cancel sign&broadcast if user doesn't want to bump the fee
514515
if (retval != QMessageBox::Yes) {

src/qt/walletview.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,10 @@ void WalletView::showOutOfSyncWarning(bool fShow)
205205

206206
void WalletView::encryptWallet()
207207
{
208-
AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this);
209-
dlg.setModel(walletModel);
210-
dlg.exec();
211-
212-
Q_EMIT encryptionStatusChanged();
208+
auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, this);
209+
dlg->setModel(walletModel);
210+
connect(dlg, &QDialog::finished, this, &WalletView::encryptionStatusChanged);
211+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
213212
}
214213

215214
void WalletView::backupWallet()
@@ -234,19 +233,18 @@ void WalletView::backupWallet()
234233

235234
void WalletView::changePassphrase()
236235
{
237-
AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
238-
dlg.setModel(walletModel);
239-
dlg.exec();
236+
auto dlg = new AskPassphraseDialog(AskPassphraseDialog::ChangePass, this);
237+
dlg->setModel(walletModel);
238+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
240239
}
241240

242241
void WalletView::unlockWallet()
243242
{
244243
// Unlock wallet when requested by wallet model
245-
if (walletModel->getEncryptionStatus() == WalletModel::Locked)
246-
{
247-
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
248-
dlg.setModel(walletModel);
249-
dlg.exec();
244+
if (walletModel->getEncryptionStatus() == WalletModel::Locked) {
245+
auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Unlock, this);
246+
dlg->setModel(walletModel);
247+
GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
250248
}
251249
}
252250

0 commit comments

Comments
 (0)