Browse Source

Make Qt shutdown gracefully on reactor stop.

Fixes #1024.
Prior to this commit, if the RPC connection were lost
while JoinmarketQt was running, the reactor would be
stopped, but the qt5reactor shutdown does not stop
the Qt Application. This commit fixes that by injecting
a custom reactor stop function wrapper into jmbase,
which triggers the close event of the Qt main window.
master
Adam Gibson 4 years ago
parent
commit
cf37639049
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 2
      jmbase/jmbase/__init__.py
  2. 17
      jmbase/jmbase/twisted_utils.py
  3. 29
      scripts/joinmarket-qt.py

2
jmbase/jmbase/__init__.py

@ -12,7 +12,7 @@ from .support import (get_log, chunks, debug_silence, jmprint,
from .proof_of_work import get_pow, verify_pow
from .twisted_utils import (stop_reactor, is_hs_uri, get_tor_agent,
get_nontor_agent, JMHiddenService,
JMHTTPResource)
JMHTTPResource, set_custom_stop_reactor)
from .bytesprod import BytesProducer
from .commands import *

17
jmbase/jmbase/twisted_utils.py

@ -9,6 +9,9 @@ import txtorcon
from txtorcon.web import tor_agent
from txtorcon import TorControlProtocol, TorConfig
_custom_stop_reactor_is_set = False
custom_stop_reactor = None
# This removes `CONF_CHANGED` requests
# over the Tor control port, which aren't needed for our use case.
def patch_add_event_listener(self, evt, callback):
@ -70,7 +73,19 @@ class WhitelistContextFactory(object):
return CertificateOptions(verify=False)
return self.default_policy.creatorForNetloc(hostname, port)
def set_custom_stop_reactor(fn):
global _custom_stop_reactor_is_set
global custom_stop_reactor
_custom_stop_reactor_is_set = True
custom_stop_reactor = fn
def stop_reactor():
if not _custom_stop_reactor_is_set:
_stop_reactor()
else:
custom_stop_reactor()
def _stop_reactor():
""" The value of the bool `reactor.running`
does not reliably tell us whether the
reactor is running (!). There are startup
@ -83,8 +98,6 @@ def stop_reactor():
except ReactorNotRunning:
pass
def is_hs_uri(s):
x = wrapped_urlparse(s)
if x.hostname.endswith(".onion"):

29
scripts/joinmarket-qt.py

@ -55,7 +55,7 @@ donation_address_url = "https://bitcoinprivacy.me/joinmarket-donations"
#Version of this Qt script specifically
JM_GUI_VERSION = '27dev'
from jmbase import get_log, stop_reactor
from jmbase import get_log, stop_reactor, set_custom_stop_reactor
from jmbase.support import EXIT_FAILURE, utxo_to_utxostr,\
hextobin, JM_CORE_VERSION
import jmbitcoin as btc
@ -1623,9 +1623,20 @@ class JMMainWindow(QMainWindow):
self.reactor = reactor
self.initUI()
# a flag to indicate that shutdown should not
# depend on user input
self.unconditional_shutdown = False
def closeEvent(self, event):
quit_msg = "Are you sure you want to quit?"
reply = JMQtMessageBox(self, quit_msg, mbtype='question')
if self.unconditional_shutdown:
JMQtMessageBox(self,
"RPC connection is lost; shutting down.",
mbtype='crit',
title="Error")
reply = QMessageBox.Yes
else:
quit_msg = "Are you sure you want to quit?"
reply = JMQtMessageBox(self, quit_msg, mbtype='question')
if reply == QMessageBox.Yes:
event.accept()
if self.reactor.threadpool is not None:
@ -2413,6 +2424,18 @@ tabWidget.currentChanged.connect(onTabChange)
mainWindow.show()
reactor.runReturn()
# Qt does not stop automatically when we stop the qt5reactor, and
# also we don't want to close without warning the user;
# patch our stop_reactor method to include the necessary cleanup:
def qt_shutdown():
# checking ensures we only fire the close
# event once even if stop_reactor is called
# multiple times (which it often is):
if mainWindow.isVisible():
mainWindow.unconditional_shutdown = True
mainWindow.close()
set_custom_stop_reactor(qt_shutdown)
# Upon launching the app, ask the user to choose a wallet to open
mainWindow.openWallet()

Loading…
Cancel
Save