Skip to content

Commit 776d48d

Browse files
committed
Merge #801: Fix nullptr clientModel access during shutdown
b7aa717 refactor: gui, simplify boost signals disconnection (furszy) f3a612f gui: guard accessing a nullptr 'clientModel' (furszy) Pull request description: Fixing #800. During shutdown, already queue events dispatched from the backend such 'numConnectionsChanged' and 0networkActiveChanged' could try to access the clientModel object, which might not exist because we manually delete it inside 'BitcoinApplication::requestShutdown()'. This happen because boost does not clears the queued events when they arise concurrently with the signal disconnection (see https://www.boost.org/doc/libs/1_55_0/doc/html/signals2/thread-safety.html). From the docs: 1) "Note that since we unlock the connection's mutex before executing its associated slot, it is possible a slot will still be executing after it has been disconnected by a [connection::disconnect](https://www.boost.org/doc/libs/1_55_0/doc/html/boost/signals2/connection.html#idp89761576-bb)(), if the disconnect was called concurrently with signal invocation." 2) "The fact that concurrent signal invocations use the same combiner object means you need to insure any custom combiner you write is thread-safe" So, we need to guard `clientModel` before accessing it at the handler side. ACKs for top commit: hebasto: re-ACK b7aa717 Tree-SHA512: f1a21d69248628f6a13556a9438c9e4ea9f0a3678aab09ddfe836e78e4eee405a6730d37d39f1445068ada3a110b655b619cf0e090fc2d0cdf99bed061364aeb
2 parents e60804f + b7aa717 commit 776d48d

File tree

5 files changed

+31
-29
lines changed

5 files changed

+31
-29
lines changed

src/qt/bitcoin.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,11 @@ void BitcoinApplication::requestShutdown()
372372
// Request node shutdown, which can interrupt long operations, like
373373
// rescanning a wallet.
374374
node().startShutdown();
375+
// Prior to unsetting the client model, stop listening backend signals
376+
if (clientModel) {
377+
clientModel->stop();
378+
}
379+
375380
// Unsetting the client model can cause the current thread to wait for node
376381
// to complete an operation, like wait for a RPC execution to complete.
377382
window->setClientModel(nullptr);

src/qt/bitcoingui.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,7 @@ void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
989989

990990
void BitcoinGUI::updateNetworkState()
991991
{
992+
if (!clientModel) return;
992993
int count = clientModel->getNumConnections();
993994
QString icon;
994995
switch(count)

src/qt/clientmodel.cpp

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,19 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
7070
subscribeToCoreSignals();
7171
}
7272

73-
ClientModel::~ClientModel()
73+
void ClientModel::stop()
7474
{
7575
unsubscribeFromCoreSignals();
7676

7777
m_thread->quit();
7878
m_thread->wait();
7979
}
8080

81+
ClientModel::~ClientModel()
82+
{
83+
stop();
84+
}
85+
8186
int ClientModel::getNumConnections(unsigned int flags) const
8287
{
8388
ConnectionDirection connections = ConnectionDirection::None;
@@ -238,47 +243,41 @@ void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockT
238243

239244
void ClientModel::subscribeToCoreSignals()
240245
{
241-
m_handler_show_progress = m_node.handleShowProgress(
246+
m_event_handlers.emplace_back(m_node.handleShowProgress(
242247
[this](const std::string& title, int progress, [[maybe_unused]] bool resume_possible) {
243248
Q_EMIT showProgress(QString::fromStdString(title), progress);
244-
});
245-
m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(
249+
}));
250+
m_event_handlers.emplace_back(m_node.handleNotifyNumConnectionsChanged(
246251
[this](int new_num_connections) {
247252
Q_EMIT numConnectionsChanged(new_num_connections);
248-
});
249-
m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(
253+
}));
254+
m_event_handlers.emplace_back(m_node.handleNotifyNetworkActiveChanged(
250255
[this](bool network_active) {
251256
Q_EMIT networkActiveChanged(network_active);
252-
});
253-
m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(
257+
}));
258+
m_event_handlers.emplace_back(m_node.handleNotifyAlertChanged(
254259
[this]() {
255260
qDebug() << "ClientModel: NotifyAlertChanged";
256261
Q_EMIT alertsChanged(getStatusBarWarnings());
257-
});
258-
m_handler_banned_list_changed = m_node.handleBannedListChanged(
262+
}));
263+
m_event_handlers.emplace_back(m_node.handleBannedListChanged(
259264
[this]() {
260265
qDebug() << "ClienModel: Requesting update for peer banlist";
261266
QMetaObject::invokeMethod(banTableModel, [this] { banTableModel->refresh(); });
262-
});
263-
m_handler_notify_block_tip = m_node.handleNotifyBlockTip(
267+
}));
268+
m_event_handlers.emplace_back(m_node.handleNotifyBlockTip(
264269
[this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
265270
TipChanged(sync_state, tip, verification_progress, SyncType::BLOCK_SYNC);
266-
});
267-
m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(
271+
}));
272+
m_event_handlers.emplace_back(m_node.handleNotifyHeaderTip(
268273
[this](SynchronizationState sync_state, interfaces::BlockTip tip, bool presync) {
269274
TipChanged(sync_state, tip, /*verification_progress=*/0.0, presync ? SyncType::HEADER_PRESYNC : SyncType::HEADER_SYNC);
270-
});
275+
}));
271276
}
272277

273278
void ClientModel::unsubscribeFromCoreSignals()
274279
{
275-
m_handler_show_progress->disconnect();
276-
m_handler_notify_num_connections_changed->disconnect();
277-
m_handler_notify_network_active_changed->disconnect();
278-
m_handler_notify_alert_changed->disconnect();
279-
m_handler_banned_list_changed->disconnect();
280-
m_handler_notify_block_tip->disconnect();
281-
m_handler_notify_header_tip->disconnect();
280+
m_event_handlers.clear();
282281
}
283282

284283
bool ClientModel::getProxyInfo(std::string& ip_port) const

src/qt/clientmodel.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class ClientModel : public QObject
5858
explicit ClientModel(interfaces::Node& node, OptionsModel *optionsModel, QObject *parent = nullptr);
5959
~ClientModel();
6060

61+
void stop();
62+
6163
interfaces::Node& node() const { return m_node; }
6264
OptionsModel *getOptionsModel();
6365
PeerTableModel *getPeerTableModel();
@@ -95,13 +97,7 @@ class ClientModel : public QObject
9597

9698
private:
9799
interfaces::Node& m_node;
98-
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
99-
std::unique_ptr<interfaces::Handler> m_handler_notify_num_connections_changed;
100-
std::unique_ptr<interfaces::Handler> m_handler_notify_network_active_changed;
101-
std::unique_ptr<interfaces::Handler> m_handler_notify_alert_changed;
102-
std::unique_ptr<interfaces::Handler> m_handler_banned_list_changed;
103-
std::unique_ptr<interfaces::Handler> m_handler_notify_block_tip;
104-
std::unique_ptr<interfaces::Handler> m_handler_notify_header_tip;
100+
std::vector<std::unique_ptr<interfaces::Handler>> m_event_handlers;
105101
OptionsModel *optionsModel;
106102
PeerTableModel* peerTableModel{nullptr};
107103
PeerTableSortProxy* m_peer_table_sort_proxy{nullptr};

src/qt/rpcconsole.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,7 @@ void RPCConsole::message(int category, const QString &message, bool html)
966966

967967
void RPCConsole::updateNetworkState()
968968
{
969+
if (!clientModel) return;
969970
QString connections = QString::number(clientModel->getNumConnections()) + " (";
970971
connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
971972
connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";

0 commit comments

Comments
 (0)