From 47f14845ca49f7eaf9fbb9672441538edc3f0722 Mon Sep 17 00:00:00 2001 From: bendikro Date: Thu, 14 Apr 2016 22:29:37 +0200 Subject: [PATCH] [GTKUI] Fix #2802: GTKUI classic mode shutdown procedure is broken Fix by leaving shutdown procedure to gtkui.py: * Daemon no longer calls component.shutdown() in GTKUI classic mode * Mainwindow no longer calls reactor.stop but instead fires a 'gtkui_close' signal. * gtkui.py installs custom SIGINT handler to initiate shutdown before stopping reactor. --- deluge/core/daemon.py | 3 ++- deluge/ui/gtkui/gtkui.py | 43 +++++++++++++++++++++++------------ deluge/ui/gtkui/mainwindow.py | 2 +- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py index 5d8f9a69b..769f8cf9a 100644 --- a/deluge/core/daemon.py +++ b/deluge/core/daemon.py @@ -154,7 +154,8 @@ class Daemon(object): def _shutdown(self, *args, **kwargs): log.info("Deluge daemon shutting down, waiting for components to shutdown...") - return component.shutdown() + if not self.classic: + return component.shutdown() @export() def get_method_list(self): diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index 3ceace614..84972ac16 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -13,12 +13,13 @@ from __future__ import division import logging import os +import signal import sys import time import gobject import gtk -from twisted.internet import gtk2reactor +from twisted.internet import defer, gtk2reactor from twisted.internet.error import ReactorAlreadyInstalledError from twisted.internet.task import LoopingCall @@ -251,36 +252,48 @@ class GtkUI(object): self.rpc_stats = LoopingCall(self.print_rpc_stats) # Twisted catches signals to terminate, so have it call a pre_shutdown method. - reactor.addSystemEventTrigger("before", "shutdown", self.pre_shutdown) + reactor.addSystemEventTrigger("before", "gtkui_close", self.close) + reactor.callWhenRunning(self._on_reactor_start) + self.closing = False + + def gtkui_sigint_handler(num, frame): + log.debug("SIGINT signal caught - firing event: 'gtkui_close'") + reactor.callLater(0, reactor.fireSystemEvent, 'gtkui_close') + + signal.signal(signal.SIGINT, gtkui_sigint_handler) # Initialize gdk threading gtk.gdk.threads_enter() reactor.run() - self.shutdown() + # Reactor is not running. Any async callbacks (Deferreds) can no longer + # be processed from this point on. gtk.gdk.threads_leave() def shutdown(self, *args, **kwargs): - log.debug("gtkui shutting down..") - - component.stop() - - # Process any pending gtk events since the mainloop has been quit - while gtk.events_pending(): - gtk.main_iteration(0) - + log.debug("GTKUI shutting down...") # Shutdown all components - component.shutdown() + if self.started_in_classic: + return component.shutdown() + @defer.inlineCallbacks + def close(self): + if self.closing: + return + self.closing = True # Make sure the config is saved. self.config.save() - - def pre_shutdown(self, *args, **kwargs): - """Modal dialogs can prevent the application exiting so destroy mainwindow""" # Ensure columns state is saved self.torrentview.save_state() + # Shut down components + yield self.shutdown() + + # Modal dialogs can prevent the application exiting so destroy mainwindow + # Must do this here to avoid hang when closing with SIGINT (CTRL-C) self.mainwindow.window.destroy() + reactor.stop() + def print_rpc_stats(self): if not client.connected(): return diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index bd561e622..4c9930f75 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -215,7 +215,7 @@ class MainWindow(component.Component): def quit_gtkui(): def stop_gtk_reactor(result=None): try: - reactor.stop() + reactor.callLater(0, reactor.fireSystemEvent, 'gtkui_close') except ReactorNotRunning: log.debug("Attempted to stop the reactor but it is not running...")