From f9d4f4a434a7a56be34bd413bd751ec5fd9043ae Mon Sep 17 00:00:00 2001 From: mhagger Date: Fri, 14 Jun 2013 07:04:37 +0000 Subject: [PATCH] Centralize gc policy in a new GarbageCollectionPolicy class. git-svn-id: http://cvs2svn.tigris.org/svn/cvs2svn/trunk@5430 be7e6eca-30d4-0310-a8e5-ac0d63af7087 --- cvs2svn_lib/main.py | 13 ------- cvs2svn_lib/pass_manager.py | 82 +++++++++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 35 deletions(-) diff --git a/cvs2svn_lib/main.py b/cvs2svn_lib/main.py index b4df8296..469eeb86 100644 --- a/cvs2svn_lib/main.py +++ b/cvs2svn_lib/main.py @@ -18,7 +18,6 @@ import os import tempfile import errno -import gc try: # Try to get access to a bunch of encodings for use with --encoding. @@ -38,18 +37,6 @@ from cvs2svn_lib.passes import passes def main(progname, run_options, pass_manager): - # Disable garbage collection, as we do not not create any circular - # data structures. To verify this assumption, the function - # check_for_garbage() in pass_manager.py is run after every pass. - # It verifies that no unreachable objects have been created (or - # reports any that were found): - try: - gc.disable() - except (AttributeError, NotImplementedError): - # Other Python implementations implement garbage collection - # differently, so if an error occurs just ignore it. - pass - # Convenience var, so we don't have to keep instantiating this Borg. ctx = Ctx() diff --git a/cvs2svn_lib/pass_manager.py b/cvs2svn_lib/pass_manager.py index 1af0a50d..e223ce63 100644 --- a/cvs2svn_lib/pass_manager.py +++ b/cvs2svn_lib/pass_manager.py @@ -35,30 +35,67 @@ class InvalidPassError(FatalError): self, msg + '\nUse --help-passes for more information.') -def check_for_garbage(): - # We've turned off the garbage collector because we shouldn't - # need it (we don't create circular dependencies) and because it - # is therefore a waste of time. So here we check for any - # unreachable objects and generate a debug-level warning if any - # occur: - try: - gc.set_debug(gc.DEBUG_SAVEALL) - gc_count = gc.collect() - if gc_count: - if logger.is_on(logger.DEBUG): - logger.debug( - 'INTERNAL: %d unreachable object(s) were garbage collected:' - % (gc_count,) - ) - for g in gc.garbage: - logger.debug(' %s' % (g,)) - del gc.garbage[:] - except (AttributeError, NotImplementedError): - # Other Python implementations implement garbage collection - # differently, so if errors occur just ignore them. +class GarbageCollectionPolicy(object): + """Defines how garbage is to be handled. + + This version just lets the Python garbage collector do its thing.""" + + def check_for_garbage(self): pass +class NoGarbageCollectionPolicy(GarbageCollectionPolicy): + """Disable the Python garbage collector. + + When check_for_garbage() is called, run the garbage collector once + to verify that no garbage has been created since the last call. If + any garbage was found, log it at the DEBUG level. + + Since cvs2svn does not not create any circular data structures, + CPython's reference-counting works perfectly and garbage collection + is unnecessary. But the automatic runs of the garbage collector + have a very measurable performance cost. So we want to turn off + garbage collection. + + However, it would be easy for a programming error to cause a + circular data structure to be created, creating garbage. So the + check_for_garbage() method is run once per pass to see whether + garbage was indeed created. If so, it reports the error at DEBUG + level. + + """ + + def __init__(self): + try: + gc.disable() + except (AttributeError, NotImplementedError): + # Other Python implementations implement garbage collection + # differently, so if an error occurs just ignore it. + pass + + def check_for_garbage(self): + """Check for any unreachable objects. + + Generate a DEBUG-level warning if any were found.""" + + try: + gc.set_debug(gc.DEBUG_SAVEALL) + gc_count = gc.collect() + if gc_count: + if logger.is_on(logger.DEBUG): + logger.debug( + 'INTERNAL: %d unreachable object(s) were garbage collected:' + % (gc_count,) + ) + for g in gc.garbage: + logger.debug(' %s' % (g,)) + del gc.garbage[:] + except (AttributeError, NotImplementedError): + # Other Python implementations implement garbage collection + # differently, so if errors occur just ignore them. + pass + + class Pass(object): """Base class for one step of the conversion.""" @@ -103,6 +140,7 @@ class PassManager: self.passes = passes self.num_passes = len(self.passes) + self.garbage_collection_policy = NoGarbageCollectionPolicy() def get_pass_number(self, pass_name, default=None): """Return the number of the pass indicated by PASS_NAME. @@ -198,7 +236,7 @@ class PassManager: # longer needed: artifact_manager.pass_done(the_pass, Ctx().skip_cleanup) - check_for_garbage() + self.garbage_collection_policy.check_for_garbage() # Tell the artifact manager about passes that are being deferred: for the_pass in self.passes[index_end:]: -- 2.11.4.GIT