Move quitter/signal/crash_handler out of qApp.

This commit is contained in:
Florian Bruhin 2015-05-06 08:51:44 +02:00
parent 564a589bc6
commit 3b5b49daac
2 changed files with 49 additions and 33 deletions

View File

@ -66,8 +66,22 @@ def run(args):
print(version.GPL_BOILERPLATE.strip())
sys.exit(0)
quitter = Quitter(args)
objreg.register('quitter', quitter)
global qApp
qApp = Application(args)
qApp.lastWindowClosed.connect(quitter.on_last_window_closed)
crash_handler = crashsignal.CrashHandler(
app=qApp, quitter=quitter, args=args, parent=qApp)
crash_handler.activate()
objreg.register('crash-handler', crash_handler)
signal_handler = crashsignal.SignalHandler(app=qApp, quitter=quitter,
parent=qApp)
signal_handler.activate()
objreg.register('signal-handler', signal_handler)
try:
sent = ipc.send_to_running_instance(args.command)
@ -93,7 +107,7 @@ def run(args):
# We didn't really initialize much so far, so we just quit hard.
sys.exit(1)
init(args)
init(args, crash_handler)
ret = qt_mainloop()
return ret
@ -107,8 +121,13 @@ def qt_mainloop():
return qApp.exec_()
def init(args):
"""Initialize everything."""
def init(args, crash_handler):
"""Initialize everything.
Args:
args: The argparse namespace.
crash_handler: The CrashHandler instance.
"""
log.init.debug("Starting init...")
qApp.setQuitOnLastWindowClosed(False)
qApp.setOrganizationName("qutebrowser")
@ -118,7 +137,7 @@ def init(args):
utils.actute_warning()
try:
_init_modules(args)
_init_modules(args, crash_handler)
except (OSError, UnicodeDecodeError) as e:
msgbox = QMessageBox(
QMessageBox.Critical, "Error while initializing!",
@ -143,7 +162,7 @@ def init(args):
QDesktopServices.setUrlHandler('qute', open_desktopservices_url)
log.init.debug("Init done!")
qApp.crash_handler.raise_crashdlg()
crash_handler.raise_crashdlg()
def _init_icon():
@ -342,8 +361,13 @@ def _maybe_hide_mouse_cursor():
qApp.restoreOverrideCursor()
def _init_modules(args):
"""Initialize all 'modules' which need to be initialized."""
def _init_modules(args, crash_handler):
"""Initialize all 'modules' which need to be initialized.
Args:
args: The argparse namespace.
crash_handler: The CrashHandler instance.
"""
# pylint: disable=too-many-statements
log.init.debug("Initializing save manager...")
save_manager = savemanager.SaveManager(qApp)
@ -362,7 +386,7 @@ def _init_modules(args):
log.init.debug("Initializing web history...")
history.init(qApp)
log.init.debug("Initializing crashlog...")
qApp.crash_handler.handle_segfault()
crash_handler.handle_segfault()
log.init.debug("Initializing sessions...")
sessions.init(qApp)
log.init.debug("Initializing js-bridge...")
@ -573,6 +597,9 @@ class Quitter:
def _shutdown(self, status): # noqa
"""Second stage of shutdown."""
log.destroy.debug("Stage 2 of shutting down...")
if qApp is None:
# No QApplication exists yet, so quit hard.
sys.exit(status)
# Remove eventfilter
try:
log.destroy.debug("Removing eventfilter...")
@ -602,14 +629,14 @@ class Quitter:
"Error while saving {}: {}".format(key, e))
msgbox.exec_()
# Re-enable faulthandler to stdout, then remove crash log
log.destroy.debug("Deactiving crash log...")
qApp.crash_handler.destroy_crashlogfile()
log.destroy.debug("Deactivating crash log...")
objreg.get('crash-handler').destroy_crashlogfile()
# If we don't kill our custom handler here we might get segfaults
log.destroy.debug("Deactiving message handler...")
qInstallMessageHandler(None)
# Now we can hopefully quit without segfaults
log.destroy.debug("Deferring QApplication::exit...")
qApp.signal_handler.deactivate()
objreg.get('signal-handler').deactivate()
# We use a singleshot timer to exit here to minimize the likelihood of
# segfaults.
QTimer.singleShot(0, functools.partial(qApp.exit, status))
@ -630,11 +657,7 @@ class Application(QApplication):
"""Main application instance.
Attributes:
quitter: The Quitter objet.
crash_handler: The CrashHandler being used.
signal_handler: The SignalHandler being used.
_args: ArgumentParser instance.
_shutting_down: True if we're currently shutting down.
"""
def __init__(self, args):
@ -648,17 +671,6 @@ class Application(QApplication):
super().__init__(qt_args)
log.init.debug("Initializing application...")
self.quitter = Quitter(args)
self.lastWindowClosed.connect(self.quitter.on_last_window_closed)
objreg.register('quitter', self.quitter)
self.crash_handler = crashsignal.CrashHandler(app=self, args=args,
parent=self)
self.crash_handler.activate()
objreg.register('crash-handler', self.crash_handler)
self.signal_handler = crashsignal.SignalHandler(app=self, parent=self)
self.signal_handler.activate()
self._args = args
objreg.register('args', args)

View File

@ -43,14 +43,16 @@ class CrashHandler(QObject):
Attributes:
_app: The QApplication instance.
_quitter: The Quitter instance.
_args: The argparse namespace.
_crash_dialog: The CrashDialog currently being shown.
_crash_log_file: The file handle for the faulthandler crash log.
"""
def __init__(self, app, args, parent=None):
def __init__(self, *, app, quitter, args, parent=None):
super().__init__(parent)
self._app = app
self._quitter = quitter
self._args = args
self._crash_log_file = None
self._crash_dialog = None
@ -160,7 +162,7 @@ class CrashHandler(QObject):
exc = (exctype, excvalue, tb)
qapp = QApplication.instance()
if not qapp.quitter.quit_status['crash']:
if not self._quitter.quit_status['crash']:
log.misc.error("ARGH, there was an exception while the crash "
"dialog is already shown:", exc_info=exc)
return
@ -185,7 +187,7 @@ class CrashHandler(QObject):
qapp.quit()
return
qapp.quitter.quit_status['crash'] = False
self._quitter.quit_status['crash'] = False
try:
pages = self._recover_pages(forgiving=True)
@ -212,7 +214,7 @@ class CrashHandler(QObject):
try:
self._app.lastWindowClosed.disconnect(
self._app.quitter.on_last_window_closed)
self._quitter.on_last_window_closed)
except TypeError:
log.destroy.exception("Error while preventing shutdown")
self._app.closeAllWindows()
@ -220,7 +222,7 @@ class CrashHandler(QObject):
self._args.debug, pages, cmd_history, exc, objects)
ret = self._crash_dialog.exec_()
if ret == QDialog.Accepted: # restore
self._app.quitter.restart(pages)
self._quitter.restart(pages)
# We might risk a segfault here, but that's better than continuing to
# run in some undefined state, so we only do the most needed shutdown
@ -241,6 +243,7 @@ class SignalHandler(QObject):
Attributes:
_app: The QApplication instance.
_quitter: The Quitter instance.
_activated: Whether activate() was called.
_notifier: A QSocketNotifier used for signals on Unix.
_timer: A QTimer used to poll for signals on Windows.
@ -248,9 +251,10 @@ class SignalHandler(QObject):
_orig_wakeup_fd: The original wakeup filedescriptor.
"""
def __init__(self, app, parent=None):
def __init__(self, *, app, quitter, parent=None):
super().__init__(parent)
self._app = app
self._quitter = quitter
self._notifier = None
self._timer = usertypes.Timer(self, 'python_hacks')
self._orig_handlers = {}
@ -330,7 +334,7 @@ class SignalHandler(QObject):
signal.signal(signal.SIGTERM, self.interrupt_forcefully)
# If we call shutdown directly here, we get a segfault.
QTimer.singleShot(0, functools.partial(
self._app.quitter.shutdown, 128 + signum))
self._quitter.shutdown, 128 + signum))
def interrupt_forcefully(self, signum, _frame):
"""Interrupt forcefully on the second SIGINT/SIGTERM request.