Disable automatic garbage collection, instead ensure garbage collection runs only in the GUI thread

This commit is contained in:
Kovid Goyal 2011-03-15 18:39:14 -06:00
parent 428ed899fc
commit e937dccaa3
2 changed files with 63 additions and 13 deletions

View File

@ -1,10 +1,14 @@
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import StringIO, traceback, sys
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
QAction, QMenu, QMenuBar, QIcon, pyqtSignal
import StringIO, traceback, sys, gc
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.utils.config import OptionParser
from calibre.gui2 import error_dialog
@ -35,6 +39,53 @@ class DebugWindow(ConversionErrorDialog):
def flush(self):
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):
___menu_bar = None
@ -64,19 +115,15 @@ class MainWindow(QMainWindow):
quit_action.setMenuRole(QAction.QuitRole)
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)
app = QCoreApplication.instance()
if app is not None:
self.connect(app, SIGNAL('unixSignal(int)'), self.unix_signal)
if disable_automatic_gc:
self._gc = GarbageCollector(self, debug=False)
if getattr(opts, 'redirect', False):
self.__console_redirect = DebugWindow(self)
sys.stdout = sys.stderr = self.__console_redirect
self.__console_redirect.show()
def unix_signal(self, signal):
print 'Received signal:', repr(signal)
def unhandled_exception(self, type, value, tb):
if type == KeyboardInterrupt:
self.keyboard_interrupt.emit()

View File

@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
'''The main GUI'''
import collections, os, sys, textwrap, time
import collections, os, sys, textwrap, time, gc
from Queue import Queue, Empty
from threading import Thread
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):
MainWindow.__init__(self, opts, parent)
MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True)
self.opts = opts
self.device_connected = None
self.gui_debug = gui_debug
@ -298,6 +298,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
raise
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:
info_dialog(self, _('Debug mode'), '<p>' +
_('You have started calibre in debug mode. After you '
@ -399,6 +402,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
elif msg.startswith('refreshdb:'):
self.library_view.model().refresh()
self.library_view.model().research()
self.tags_view.recount()
else:
print msg
@ -465,7 +469,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.device_manager.set_current_library_uuid(db.library_id)
# Run a garbage collection now so that it does not freeze the
# interface later
import gc
gc.collect()