From 6373959824529a0cfa18df53a9d02b0fbe7859a5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 6 Dec 2022 18:44:01 +0100 Subject: Always delay stage 2 shutdown, not only with prompts If we shut down qutebrowser directly, in some rare situations, we seem to get into some trouble related to a QWidget trying to call into a Q(Common|StyleSheet)Style at shutdown (checking the style hint SH_Widget_ShareActivation, to figure out the palette to use when the window is hidden, or somesuch?). With the recent changes for #7504, those crashes suddenly started happening much more often. After staring at the related changes and gdb for over a day, I still have no idea what actually causes them and why it's crashing - it smells like some kind of PyQt/Qt cleanup order bug... However, what I *did* find out (after a lot of painful debugging and messing around with things) is that delaying the shutdown done from :quit seems to reliably get rid of the issue. I don't know *why*, but I haven't seen it happen anymore with the latest changes... Fixes #5385 and #5124, both speculatively. Closes #7504 since this was the last change required to make it all work. --- qutebrowser/mainwindow/prompt.py | 16 ++++------------ qutebrowser/misc/quitter.py | 27 ++++++++++++--------------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 279721b2a..db7553e7e 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -131,20 +131,12 @@ class PromptQueue(QObject): """Cancel all blocking questions. Quits and removes all running event loops. - - Return: - True if loops needed to be aborted, - False otherwise. """ - log.prompt.debug("Shutting down with loops {}".format(self._loops)) + log.prompt.debug(f"Shutting down with loops {self._loops}") self._shutting_down = True - if self._loops: - for loop in self._loops: - loop.quit() - loop.deleteLater() - return True - else: - return False + for loop in self._loops: + loop.quit() + loop.deleteLater() @pyqtSlot(usertypes.Question, bool) def ask_question(self, question, blocking): diff --git a/qutebrowser/misc/quitter.py b/qutebrowser/misc/quitter.py index 92764a3a4..a849aec70 100644 --- a/qutebrowser/misc/quitter.py +++ b/qutebrowser/misc/quitter.py @@ -221,21 +221,18 @@ class Quitter(QObject): status, session)) sessions.shutdown(session, last_window=last_window) - - if prompt.prompt_queue.shutdown(): - # If shutdown was called while we were asking a question, we're in - # a still sub-eventloop (which gets quit now) and not in the main - # one. - # This means we need to defer the real shutdown to when we're back - # in the real main event loop, or we'll get a segfault. - log.destroy.debug("Deferring real shutdown because question was " - "active.") - QTimer.singleShot(0, functools.partial(self._shutdown_2, status, - is_restart=is_restart)) - else: - # If we have no questions to shut down, we are already in the real - # event loop, so we can shut down immediately. - self._shutdown_2(status, is_restart=is_restart) + prompt.prompt_queue.shutdown() + + # If shutdown was called while we were asking a question, we're in + # a still sub-eventloop (which gets quit now) and not in the main + # one. + # But there's also other situations where it's problematic to shut down + # immediately (e.g. when we're just starting up). + # This means we need to defer the real shutdown to when we're back + # in the real main event loop, or we'll get a segfault. + log.destroy.debug("Deferring shutdown stage 2") + QTimer.singleShot( + 0, functools.partial(self._shutdown_2, status, is_restart=is_restart)) def _shutdown_2(self, status: int, is_restart: bool) -> None: """Second stage of shutdown.""" -- cgit v1.2.3-54-g00ecf