[Checkins] SVN: transaction/trunk/ - Code in ``_transaction.py`` held on to local references to traceback

Chris McDonough chrism at plope.com
Mon Sep 13 16:15:11 EDT 2010


Log message for revision 116343:
  - Code in ``_transaction.py`` held on to local references to traceback
    objects after calling ``sys.exc_info()`` to get one, causing
    potential reference leakages.
  
  

Changed:
  U   transaction/trunk/CHANGES.txt
  U   transaction/trunk/transaction/_transaction.py

-=-
Modified: transaction/trunk/CHANGES.txt
===================================================================
--- transaction/trunk/CHANGES.txt	2010-09-13 14:42:48 UTC (rev 116342)
+++ transaction/trunk/CHANGES.txt	2010-09-13 20:15:10 UTC (rev 116343)
@@ -1,6 +1,15 @@
 Changes
 =======
 
+Next release
+------------
+
+Bug Fixes:
+
+- Code in ``_transaction.py`` held on to local references to traceback
+  objects after calling ``sys.exc_info()`` to get one, causing
+  potential reference leakages.
+
 1.1.0 (1010-05-12)
 ------------------
 

Modified: transaction/trunk/transaction/_transaction.py
===================================================================
--- transaction/trunk/transaction/_transaction.py	2010-09-13 14:42:48 UTC (rev 116342)
+++ transaction/trunk/transaction/_transaction.py	2010-09-13 20:15:10 UTC (rev 116343)
@@ -329,9 +329,15 @@
             self._commitResources()
             self.status = Status.COMMITTED
         except:
-            t, v, tb = self._saveAndGetCommitishError()
-            self._callAfterCommitHooks(status=False)
-            raise t, v, tb
+            t = None
+            v = None
+            tb = None
+            try:
+                t, v, tb = self._saveAndGetCommitishError()
+                self._callAfterCommitHooks(status=False)
+                raise t, v, tb
+            finally:
+                del t, v, tb
         else:
             if self._manager:
                 self._manager.free(self)
@@ -343,14 +349,21 @@
         self.status = Status.COMMITFAILED
         # Save the traceback for TransactionFailedError.
         ft = self._failure_traceback = StringIO()
-        t, v, tb = sys.exc_info()
-        # Record how we got into commit().
-        traceback.print_stack(sys._getframe(1), None, ft)
-        # Append the stack entries from here down to the exception.
-        traceback.print_tb(tb, None, ft)
-        # Append the exception type and value.
-        ft.writelines(traceback.format_exception_only(t, v))
-        return t, v, tb
+        t = None
+        v = None
+        tb = None
+        try:
+            t, v, tb = sys.exc_info()
+            # Record how we got into commit().
+            traceback.print_stack(sys._getframe(1), None, ft)
+            # Append the stack entries from here down to the exception.
+            traceback.print_tb(tb, None, ft)
+            # Append the exception type and value.
+            ft.writelines(traceback.format_exception_only(t, v))
+            return t, v, tb
+        finally:
+            del t, v, tb
+        
 
     def _saveAndRaiseCommitishError(self):
         t, v, tb = self._saveAndGetCommitishError()
@@ -442,10 +455,13 @@
             # to revert the changes in each of the resource managers.
             t, v, tb = sys.exc_info()
             try:
-                self._cleanup(L)
+                try:
+                    self._cleanup(L)
+                finally:
+                    self._synchronizers.map(lambda s: s.afterCompletion(self))
+                raise t, v, tb
             finally:
-                self._synchronizers.map(lambda s: s.afterCompletion(self))
-            raise t, v, tb
+                del t, v, tb
 
     def _cleanup(self, L):
         # Called when an exception occurs during tpc_vote or tpc_finish.
@@ -469,26 +485,33 @@
 
         self._synchronizers.map(lambda s: s.beforeCompletion(self))
 
-        tb = None
-        for rm in self._resources:
-            try:
-                rm.abort(self)
-            except:
-                if tb is None:
-                    t, v, tb = sys.exc_info()
-                self.log.error("Failed to abort resource manager: %s",
-                               rm, exc_info=sys.exc_info())
+        try:
 
-        if self._manager:
-            self._manager.free(self)
+            t = None
+            v = None
+            tb = None
+            
+            for rm in self._resources:
+                try:
+                    rm.abort(self)
+                except:
+                    if tb is None:
+                        t, v, tb = sys.exc_info()
+                    self.log.error("Failed to abort resource manager: %s",
+                                   rm, exc_info=sys.exc_info())
 
-        self._synchronizers.map(lambda s: s.afterCompletion(self))
+            if self._manager:
+                self._manager.free(self)
 
-        self.log.debug("abort")
+            self._synchronizers.map(lambda s: s.afterCompletion(self))
 
-        if tb is not None:
-            raise t, v, tb
+            self.log.debug("abort")
 
+            if tb is not None:
+                raise t, v, tb
+        finally:
+            del t, v, tb
+
     def note(self, text):
         text = text.strip()
         if self.description:
@@ -542,20 +565,26 @@
         self.manager.tpc_vote(txn)
 
     def abort(self, txn):
+        t = None
+        v = None
         tb = None
-        for o in self.objects:
-            try:
-                self.manager.abort(o, txn)
-            except:
-                # Capture the first exception and re-raise it after
-                # aborting all the other objects.
-                if tb is None:
-                    t, v, tb = sys.exc_info()
-                txn.log.error("Failed to abort object: %s",
-                              object_hint(o), exc_info=sys.exc_info())
-        if tb is not None:
-            raise t, v, tb
+        try:
+            for o in self.objects:
+                try:
+                    self.manager.abort(o, txn)
+                except:
+                    # Capture the first exception and re-raise it after
+                    # aborting all the other objects.
+                    if tb is None:
+                        t, v, tb = sys.exc_info()
+                    txn.log.error("Failed to abort object: %s",
+                                  object_hint(o), exc_info=sys.exc_info())
 
+            if tb is not None:
+                raise t, v, tb
+        finally:
+            del t, v, tb
+
 def rm_cmp(rm1, rm2):
     return cmp(rm1.sortKey(), rm2.sortKey())
 



More information about the checkins mailing list