Skip to content

Commit c12f4e9

Browse files
laanwjluke-jr
authored andcommitted
qt: Prevent thread/memory leak on exiting RPCConsole
Make ownership of the QThread object clear, so that the RPCConsole can wait for the executor thread to quit before shutdown is called. This increases overall thread safety, and prevents some objects from leaking on exit. Github-Pull: #9190 Rebased-From: 693384e
1 parent dc46b10 commit c12f4e9

File tree

4 files changed

+27
-13
lines changed

4 files changed

+27
-13
lines changed

src/qt/bitcoin.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,11 @@ void BitcoinApplication::requestInitialize()
409409

410410
void BitcoinApplication::requestShutdown()
411411
{
412+
// Show a simple window indicating shutdown status
413+
// Do this first as some of the steps may take some time below,
414+
// for example the RPC console may still be executing a command.
415+
ShutdownWindow::showShutdownWindow(window);
416+
412417
qDebug() << __func__ << ": Requesting shutdown";
413418
startThread();
414419
window->hide();
@@ -423,9 +428,6 @@ void BitcoinApplication::requestShutdown()
423428
delete clientModel;
424429
clientModel = 0;
425430

426-
// Show a simple window indicating shutdown status
427-
ShutdownWindow::showShutdownWindow(window);
428-
429431
// Request shutdown from core thread
430432
Q_EMIT requestedShutdown();
431433
}

src/qt/bitcoingui.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,12 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
496496
// Disable context menu on tray icon
497497
trayIconMenu->clear();
498498
}
499+
// Propagate cleared model to child objects
500+
rpcConsole->setClientModel(nullptr);
501+
#ifdef ENABLE_WALLET
502+
walletFrame->setClientModel(nullptr);
503+
#endif // ENABLE_WALLET
504+
unitDisplayControl->setOptionsModel(nullptr);
499505
}
500506
}
501507

src/qt/rpcconsole.cpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
288288
// based timer interface
289289
RPCSetTimerInterfaceIfUnset(rpcTimerInterface);
290290

291-
startExecutor();
292291
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
293292

294293
ui->detailWidget->hide();
@@ -302,7 +301,6 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
302301
RPCConsole::~RPCConsole()
303302
{
304303
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
305-
Q_EMIT stopExecutor();
306304
RPCUnsetTimerInterface(rpcTimerInterface);
307305
delete rpcTimerInterface;
308306
delete ui;
@@ -466,6 +464,14 @@ void RPCConsole::setClientModel(ClientModel *model)
466464
autoCompleter = new QCompleter(wordList, this);
467465
ui->lineEdit->setCompleter(autoCompleter);
468466
autoCompleter->popup()->installEventFilter(this);
467+
// Start thread to execute RPC commands.
468+
startExecutor();
469+
}
470+
if (!model) {
471+
// Client model is being set to 0, this means shutdown() is about to be called.
472+
// Make sure we clean up the executor thread
473+
Q_EMIT stopExecutor();
474+
thread.wait();
469475
}
470476
}
471477

@@ -646,26 +652,24 @@ void RPCConsole::browseHistory(int offset)
646652

647653
void RPCConsole::startExecutor()
648654
{
649-
QThread *thread = new QThread;
650655
RPCExecutor *executor = new RPCExecutor();
651-
executor->moveToThread(thread);
656+
executor->moveToThread(&thread);
652657

653658
// Replies from executor object must go to this object
654659
connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString)));
655660
// Requests from this object must go to executor
656661
connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString)));
657662

658663
// On stopExecutor signal
659-
// - queue executor for deletion (in execution thread)
660664
// - quit the Qt event loop in the execution thread
661-
connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater()));
662-
connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit()));
663-
// Queue the thread for deletion (in this thread) when it is finished
664-
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
665+
connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit()));
666+
// - queue executor for deletion (in execution thread)
667+
connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()), Qt::DirectConnection);
668+
connect(&thread, SIGNAL(finished()), this, SLOT(test()), Qt::DirectConnection);
665669

666670
// Default implementation of QThread::run() simply spins up an event loop in the thread,
667671
// which is what we want.
668-
thread->start();
672+
thread.start();
669673
}
670674

671675
void RPCConsole::on_tabWidget_currentChanged(int index)

src/qt/rpcconsole.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <QWidget>
1414
#include <QCompleter>
15+
#include <QThread>
1516

1617
class ClientModel;
1718
class PlatformStyle;
@@ -140,6 +141,7 @@ public Q_SLOTS:
140141
QMenu *banTableContextMenu;
141142
int consoleFontSize;
142143
QCompleter *autoCompleter;
144+
QThread thread;
143145
};
144146

145147
#endif // BITCOIN_QT_RPCCONSOLE_H

0 commit comments

Comments
 (0)