mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Linux/OSX: Shutdown gracefully on receiving interrupt or terminate signals
This commit is contained in:
parent
57efd828ef
commit
ece360f04c
@ -1,13 +1,13 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
""" The GUI """
|
||||
import os, sys, Queue, threading, glob
|
||||
import os, sys, Queue, threading, glob, signal
|
||||
from contextlib import contextmanager
|
||||
from threading import RLock, Lock
|
||||
from urllib import unquote
|
||||
from PyQt5.QtWidgets import QStyle # Gives a nicer error message than import from Qt
|
||||
from PyQt5.Qt import (
|
||||
QFileInfo, QObject, QBuffer, Qt, QByteArray, QTranslator,
|
||||
QFileInfo, QObject, QBuffer, Qt, QByteArray, QTranslator, QSocketNotifier,
|
||||
QCoreApplication, QThread, QEvent, QTimer, pyqtSignal, QDateTime,
|
||||
QDesktopServices, QFileDialog, QFileIconProvider, QSettings, QIcon,
|
||||
QApplication, QDialog, QUrl, QFont, QFontDatabase, QLocale, QFontInfo)
|
||||
@ -860,6 +860,8 @@ def setup_gui_option_parser(parser):
|
||||
|
||||
class Application(QApplication):
|
||||
|
||||
shutdown_signal_received = pyqtSignal()
|
||||
|
||||
def __init__(self, args, force_calibre_style=False, override_program_name=None, headless=False, color_prefs=gprefs):
|
||||
self.file_event_hook = None
|
||||
if override_program_name:
|
||||
@ -872,6 +874,8 @@ class Application(QApplication):
|
||||
qargs = [i.encode('utf-8') if isinstance(i, unicode) else i for i in args]
|
||||
self.pi = plugins['progress_indicator'][0]
|
||||
QApplication.__init__(self, qargs)
|
||||
if not iswindows:
|
||||
self.setup_unix_signals()
|
||||
if islinux or isbsd:
|
||||
self.setAttribute(Qt.AA_DontUseNativeMenuBar, 'CALIBRE_NO_NATIVE_MENUBAR' in os.environ)
|
||||
self.setup_styles(force_calibre_style)
|
||||
@ -1048,6 +1052,28 @@ class Application(QApplication):
|
||||
def __exit__(self, *args):
|
||||
self.setQuitOnLastWindowClosed(True)
|
||||
|
||||
def setup_unix_signals(self):
|
||||
import fcntl
|
||||
read_fd, write_fd = os.pipe()
|
||||
cloexec_flag = getattr(fcntl, 'FD_CLOEXEC', 1)
|
||||
for fd in (read_fd, write_fd):
|
||||
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||
fcntl.fcntl(fd, fcntl.F_SETFD, flags | cloexec_flag | os.O_NONBLOCK)
|
||||
for sig in (signal.SIGINT, signal.SIGTERM):
|
||||
signal.signal(sig, lambda x, y: None)
|
||||
signal.siginterrupt(sig, False)
|
||||
signal.set_wakeup_fd(write_fd)
|
||||
self.signal_notifier = QSocketNotifier(read_fd, QSocketNotifier.Read, self)
|
||||
self.signal_notifier.setEnabled(True)
|
||||
self.signal_notifier.activated.connect(self.signal_received, type=Qt.QueuedConnection)
|
||||
|
||||
def signal_received(self, read_fd):
|
||||
try:
|
||||
os.read(read_fd, 1024)
|
||||
except EnvironmentError:
|
||||
return
|
||||
self.shutdown_signal_received.emit()
|
||||
|
||||
_store_app = None
|
||||
|
||||
@contextmanager
|
||||
|
@ -86,7 +86,6 @@ class MainWindow(QMainWindow):
|
||||
___menu = None
|
||||
__actions = []
|
||||
|
||||
keyboard_interrupt = pyqtSignal()
|
||||
# See https://bugreports.qt-project.org/browse/QTBUG-42281
|
||||
window_blocked = pyqtSignal()
|
||||
window_unblocked = pyqtSignal()
|
||||
@ -131,8 +130,7 @@ class MainWindow(QMainWindow):
|
||||
sys.excepthook = ExceptionHandler(self)
|
||||
|
||||
def unhandled_exception(self, type, value, tb):
|
||||
if type == KeyboardInterrupt:
|
||||
self.keyboard_interrupt.emit()
|
||||
if type is KeyboardInterrupt:
|
||||
return
|
||||
try:
|
||||
sio = StringIO.StringIO()
|
||||
|
@ -178,7 +178,7 @@ def main(control_conn, data_conn):
|
||||
while True:
|
||||
try:
|
||||
request = eintr_retry_call(control_conn.recv)
|
||||
except EOFError:
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
break
|
||||
if request is None:
|
||||
break
|
||||
|
@ -71,6 +71,7 @@ def _run(args, notify=None):
|
||||
main = Main(opts, notify=notify)
|
||||
main.set_exception_handler()
|
||||
main.show()
|
||||
app.shutdown_signal_received.connect(main.boss.quit)
|
||||
if len(args) > 1:
|
||||
main.boss.open_book(args[1], edit_file=args[2:], clear_notify_data=False)
|
||||
else:
|
||||
|
@ -397,8 +397,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
if config['autolaunch_server']:
|
||||
self.start_content_server()
|
||||
|
||||
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
||||
|
||||
self.read_settings()
|
||||
self.finalize_layout()
|
||||
if self.bars_manager.showing_donate:
|
||||
@ -425,6 +423,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
# Collect cycles now
|
||||
gc.collect()
|
||||
|
||||
QApplication.instance().shutdown_signal_received.connect(self.quit)
|
||||
if show_gui and self.gui_debug is not None:
|
||||
QTimer.singleShot(10, self.show_gui_debug_msg)
|
||||
|
||||
@ -843,6 +842,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
|
||||
def quit(self, checked=True, restart=False, debug_on_restart=False,
|
||||
confirm_quit=True):
|
||||
if self.shutting_down:
|
||||
return
|
||||
if confirm_quit and not self.confirm_quit():
|
||||
return
|
||||
try:
|
||||
|
@ -178,6 +178,7 @@ class EbookViewer(MainWindow):
|
||||
self.action_reload = QAction(_('&Reload book'), self)
|
||||
self.action_reload.triggered.connect(self.reload_book)
|
||||
self.action_quit.triggered.connect(self.quit)
|
||||
QApplication.instance().shutdown_signal_received.connect(self.action_quit.trigger)
|
||||
self.action_reference_mode.triggered[bool].connect(self.view.reference_mode)
|
||||
self.action_metadata.triggered[bool].connect(self.metadata.setVisible)
|
||||
self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible)
|
||||
|
@ -337,6 +337,8 @@ def worker_main(conn):
|
||||
job = cPickle.loads(eintr_retry_call(conn.recv_bytes))
|
||||
except EOFError:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception:
|
||||
prints('recv() failed in worker, terminating worker', file=sys.stderr)
|
||||
import traceback
|
||||
|
@ -70,6 +70,8 @@ class OffloadWorker(object):
|
||||
def shutdown(self):
|
||||
try:
|
||||
eintr_retry_call(self.conn.send, None)
|
||||
except IOError:
|
||||
pass
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
Loading…
x
Reference in New Issue
Block a user