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.
This commit is contained in:
parent
47db075377
commit
693384eedb
4 changed files with 29 additions and 16 deletions
|
@ -409,6 +409,11 @@ void BitcoinApplication::requestInitialize()
|
||||||
|
|
||||||
void BitcoinApplication::requestShutdown()
|
void BitcoinApplication::requestShutdown()
|
||||||
{
|
{
|
||||||
|
// Show a simple window indicating shutdown status
|
||||||
|
// Do this first as some of the steps may take some time below,
|
||||||
|
// for example the RPC console may still be executing a command.
|
||||||
|
ShutdownWindow::showShutdownWindow(window);
|
||||||
|
|
||||||
qDebug() << __func__ << ": Requesting shutdown";
|
qDebug() << __func__ << ": Requesting shutdown";
|
||||||
startThread();
|
startThread();
|
||||||
window->hide();
|
window->hide();
|
||||||
|
@ -423,9 +428,6 @@ void BitcoinApplication::requestShutdown()
|
||||||
delete clientModel;
|
delete clientModel;
|
||||||
clientModel = 0;
|
clientModel = 0;
|
||||||
|
|
||||||
// Show a simple window indicating shutdown status
|
|
||||||
ShutdownWindow::showShutdownWindow(window);
|
|
||||||
|
|
||||||
// Request shutdown from core thread
|
// Request shutdown from core thread
|
||||||
Q_EMIT requestedShutdown();
|
Q_EMIT requestedShutdown();
|
||||||
}
|
}
|
||||||
|
|
|
@ -511,6 +511,13 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
|
||||||
// Disable context menu on tray icon
|
// Disable context menu on tray icon
|
||||||
trayIconMenu->clear();
|
trayIconMenu->clear();
|
||||||
}
|
}
|
||||||
|
// Propagate cleared model to child objects
|
||||||
|
rpcConsole->setClientModel(nullptr);
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
walletFrame->setClientModel(nullptr);
|
||||||
|
#endif // ENABLE_WALLET
|
||||||
|
unitDisplayControl->setOptionsModel(nullptr);
|
||||||
|
connectionsControl->setClientModel(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1242,7 +1249,5 @@ void NetworkToggleStatusBarControl::mousePressEvent(QMouseEvent *event)
|
||||||
/** Lets the control know about the Client Model */
|
/** Lets the control know about the Client Model */
|
||||||
void NetworkToggleStatusBarControl::setClientModel(ClientModel *_clientModel)
|
void NetworkToggleStatusBarControl::setClientModel(ClientModel *_clientModel)
|
||||||
{
|
{
|
||||||
if (_clientModel) {
|
|
||||||
this->clientModel = _clientModel;
|
this->clientModel = _clientModel;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,7 +382,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
|
||||||
// based timer interface
|
// based timer interface
|
||||||
RPCSetTimerInterfaceIfUnset(rpcTimerInterface);
|
RPCSetTimerInterfaceIfUnset(rpcTimerInterface);
|
||||||
|
|
||||||
startExecutor();
|
|
||||||
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
|
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
|
||||||
|
|
||||||
ui->detailWidget->hide();
|
ui->detailWidget->hide();
|
||||||
|
@ -396,7 +395,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
|
||||||
RPCConsole::~RPCConsole()
|
RPCConsole::~RPCConsole()
|
||||||
{
|
{
|
||||||
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
|
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
|
||||||
Q_EMIT stopExecutor();
|
|
||||||
RPCUnsetTimerInterface(rpcTimerInterface);
|
RPCUnsetTimerInterface(rpcTimerInterface);
|
||||||
delete rpcTimerInterface;
|
delete rpcTimerInterface;
|
||||||
delete ui;
|
delete ui;
|
||||||
|
@ -565,6 +563,14 @@ void RPCConsole::setClientModel(ClientModel *model)
|
||||||
autoCompleter = new QCompleter(wordList, this);
|
autoCompleter = new QCompleter(wordList, this);
|
||||||
ui->lineEdit->setCompleter(autoCompleter);
|
ui->lineEdit->setCompleter(autoCompleter);
|
||||||
autoCompleter->popup()->installEventFilter(this);
|
autoCompleter->popup()->installEventFilter(this);
|
||||||
|
// Start thread to execute RPC commands.
|
||||||
|
startExecutor();
|
||||||
|
}
|
||||||
|
if (!model) {
|
||||||
|
// Client model is being set to 0, this means shutdown() is about to be called.
|
||||||
|
// Make sure we clean up the executor thread
|
||||||
|
Q_EMIT stopExecutor();
|
||||||
|
thread.wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,9 +765,8 @@ void RPCConsole::browseHistory(int offset)
|
||||||
|
|
||||||
void RPCConsole::startExecutor()
|
void RPCConsole::startExecutor()
|
||||||
{
|
{
|
||||||
QThread *thread = new QThread;
|
|
||||||
RPCExecutor *executor = new RPCExecutor();
|
RPCExecutor *executor = new RPCExecutor();
|
||||||
executor->moveToThread(thread);
|
executor->moveToThread(&thread);
|
||||||
|
|
||||||
// Replies from executor object must go to this object
|
// Replies from executor object must go to this object
|
||||||
connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString)));
|
connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString)));
|
||||||
|
@ -769,16 +774,15 @@ void RPCConsole::startExecutor()
|
||||||
connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString)));
|
connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString)));
|
||||||
|
|
||||||
// On stopExecutor signal
|
// On stopExecutor signal
|
||||||
// - queue executor for deletion (in execution thread)
|
|
||||||
// - quit the Qt event loop in the execution thread
|
// - quit the Qt event loop in the execution thread
|
||||||
connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater()));
|
connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit()));
|
||||||
connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit()));
|
// - queue executor for deletion (in execution thread)
|
||||||
// Queue the thread for deletion (in this thread) when it is finished
|
connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()), Qt::DirectConnection);
|
||||||
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
connect(&thread, SIGNAL(finished()), this, SLOT(test()), Qt::DirectConnection);
|
||||||
|
|
||||||
// Default implementation of QThread::run() simply spins up an event loop in the thread,
|
// Default implementation of QThread::run() simply spins up an event loop in the thread,
|
||||||
// which is what we want.
|
// which is what we want.
|
||||||
thread->start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCConsole::on_tabWidget_currentChanged(int index)
|
void RPCConsole::on_tabWidget_currentChanged(int index)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QCompleter>
|
#include <QCompleter>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
class ClientModel;
|
class ClientModel;
|
||||||
class PlatformStyle;
|
class PlatformStyle;
|
||||||
|
@ -148,6 +149,7 @@ private:
|
||||||
QMenu *banTableContextMenu;
|
QMenu *banTableContextMenu;
|
||||||
int consoleFontSize;
|
int consoleFontSize;
|
||||||
QCompleter *autoCompleter;
|
QCompleter *autoCompleter;
|
||||||
|
QThread thread;
|
||||||
|
|
||||||
/** Update UI with latest network info from model. */
|
/** Update UI with latest network info from model. */
|
||||||
void updateNetworkState();
|
void updateNetworkState();
|
||||||
|
|
Loading…
Reference in a new issue