mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Disable automatic garbage collection, instead ensure garbage collection runs only in the GUI thread
This commit is contained in:
parent
428ed899fc
commit
e937dccaa3
@ -1,10 +1,14 @@
|
|||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import StringIO, traceback, sys
|
|
||||||
|
|
||||||
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
|
import StringIO, traceback, sys, gc
|
||||||
QAction, QMenu, QMenuBar, QIcon, pyqtSignal
|
|
||||||
|
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QTimer, \
|
||||||
|
QAction, QMenu, QMenuBar, QIcon, pyqtSignal, QObject
|
||||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
@ -35,6 +39,53 @@ class DebugWindow(ConversionErrorDialog):
|
|||||||
def flush(self):
|
def flush(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class GarbageCollector(QObject):
|
||||||
|
|
||||||
|
'''
|
||||||
|
Disable automatic garbage collection and instead collect manually
|
||||||
|
every INTERVAL milliseconds.
|
||||||
|
|
||||||
|
This is done to ensure that garbage collection only happens in the GUI
|
||||||
|
thread, as otherwise Qt can crash.
|
||||||
|
'''
|
||||||
|
|
||||||
|
INTERVAL = 5000
|
||||||
|
|
||||||
|
def __init__(self, parent, debug=False):
|
||||||
|
QObject.__init__(self, parent)
|
||||||
|
self.debug = debug
|
||||||
|
|
||||||
|
self.timer = QTimer(self)
|
||||||
|
self.timer.timeout.connect(self.check)
|
||||||
|
|
||||||
|
self.threshold = gc.get_threshold()
|
||||||
|
gc.disable()
|
||||||
|
self.timer.start(self.INTERVAL)
|
||||||
|
#gc.set_debug(gc.DEBUG_SAVEALL)
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
#return self.debug_cycles()
|
||||||
|
l0, l1, l2 = gc.get_count()
|
||||||
|
if self.debug:
|
||||||
|
print ('gc_check called:', l0, l1, l2)
|
||||||
|
if l0 > self.threshold[0]:
|
||||||
|
num = gc.collect(0)
|
||||||
|
if self.debug:
|
||||||
|
print ('collecting gen 0, found:', num, 'unreachable')
|
||||||
|
if l1 > self.threshold[1]:
|
||||||
|
num = gc.collect(1)
|
||||||
|
if self.debug:
|
||||||
|
print ('collecting gen 1, found:', num, 'unreachable')
|
||||||
|
if l2 > self.threshold[2]:
|
||||||
|
num = gc.collect(2)
|
||||||
|
if self.debug:
|
||||||
|
print ('collecting gen 2, found:', num, 'unreachable')
|
||||||
|
|
||||||
|
def debug_cycles(self):
|
||||||
|
gc.collect()
|
||||||
|
for obj in gc.garbage:
|
||||||
|
print (obj, repr(obj), type(obj))
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
|
|
||||||
___menu_bar = None
|
___menu_bar = None
|
||||||
@ -64,19 +115,15 @@ class MainWindow(QMainWindow):
|
|||||||
quit_action.setMenuRole(QAction.QuitRole)
|
quit_action.setMenuRole(QAction.QuitRole)
|
||||||
return preferences_action, quit_action
|
return preferences_action, quit_action
|
||||||
|
|
||||||
def __init__(self, opts, parent=None):
|
def __init__(self, opts, parent=None, disable_automatic_gc=False):
|
||||||
QMainWindow.__init__(self, parent)
|
QMainWindow.__init__(self, parent)
|
||||||
app = QCoreApplication.instance()
|
if disable_automatic_gc:
|
||||||
if app is not None:
|
self._gc = GarbageCollector(self, debug=False)
|
||||||
self.connect(app, SIGNAL('unixSignal(int)'), self.unix_signal)
|
|
||||||
if getattr(opts, 'redirect', False):
|
if getattr(opts, 'redirect', False):
|
||||||
self.__console_redirect = DebugWindow(self)
|
self.__console_redirect = DebugWindow(self)
|
||||||
sys.stdout = sys.stderr = self.__console_redirect
|
sys.stdout = sys.stderr = self.__console_redirect
|
||||||
self.__console_redirect.show()
|
self.__console_redirect.show()
|
||||||
|
|
||||||
def unix_signal(self, signal):
|
|
||||||
print 'Received signal:', repr(signal)
|
|
||||||
|
|
||||||
def unhandled_exception(self, type, value, tb):
|
def unhandled_exception(self, type, value, tb):
|
||||||
if type == KeyboardInterrupt:
|
if type == KeyboardInterrupt:
|
||||||
self.keyboard_interrupt.emit()
|
self.keyboard_interrupt.emit()
|
||||||
|
@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
'''The main GUI'''
|
'''The main GUI'''
|
||||||
|
|
||||||
import collections, os, sys, textwrap, time
|
import collections, os, sys, textwrap, time, gc
|
||||||
from Queue import Queue, Empty
|
from Queue import Queue, Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from PyQt4.Qt import Qt, SIGNAL, QTimer, QHelpEvent, QAction, \
|
from PyQt4.Qt import Qt, SIGNAL, QTimer, QHelpEvent, QAction, \
|
||||||
@ -95,7 +95,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, opts, parent=None, gui_debug=None):
|
def __init__(self, opts, parent=None, gui_debug=None):
|
||||||
MainWindow.__init__(self, opts, parent)
|
MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True)
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.device_connected = None
|
self.device_connected = None
|
||||||
self.gui_debug = gui_debug
|
self.gui_debug = gui_debug
|
||||||
@ -298,6 +298,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
raise
|
raise
|
||||||
self.device_manager.set_current_library_uuid(db.library_id)
|
self.device_manager.set_current_library_uuid(db.library_id)
|
||||||
|
|
||||||
|
# Collect cycles now
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
if show_gui and self.gui_debug is not None:
|
if show_gui and self.gui_debug is not None:
|
||||||
info_dialog(self, _('Debug mode'), '<p>' +
|
info_dialog(self, _('Debug mode'), '<p>' +
|
||||||
_('You have started calibre in debug mode. After you '
|
_('You have started calibre in debug mode. After you '
|
||||||
@ -399,6 +402,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
elif msg.startswith('refreshdb:'):
|
elif msg.startswith('refreshdb:'):
|
||||||
self.library_view.model().refresh()
|
self.library_view.model().refresh()
|
||||||
self.library_view.model().research()
|
self.library_view.model().research()
|
||||||
|
self.tags_view.recount()
|
||||||
else:
|
else:
|
||||||
print msg
|
print msg
|
||||||
|
|
||||||
@ -465,7 +469,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.device_manager.set_current_library_uuid(db.library_id)
|
self.device_manager.set_current_library_uuid(db.library_id)
|
||||||
# Run a garbage collection now so that it does not freeze the
|
# Run a garbage collection now so that it does not freeze the
|
||||||
# interface later
|
# interface later
|
||||||
import gc
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user