[session] fix memory leak: local thread data living in a thread that never finishes (eg, the main thread) may not be properly freed stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 26 Feb 2010 07:03:38 +0100
branchstable
changeset 4704 a1ac5a453146
parent 4703 4e803c30b7db
child 4705 8ae13d059850
[session] fix memory leak: local thread data living in a thread that never finishes (eg, the main thread) may not be properly freed
server/session.py
--- a/server/session.py	Fri Feb 26 06:59:16 2010 +0100
+++ b/server/session.py	Fri Feb 26 07:03:38 2010 +0100
@@ -314,7 +314,7 @@
             except KeyError:
                 pass
             pool.pool_reset()
-            self._threaddata.pool = None
+            del self._threaddata.pool
             # free pool once everything is done to avoid race-condition
             self.repo._free_pool(pool)
 
@@ -410,13 +410,13 @@
     @property
     def super_session(self):
         try:
-            csession = self._threaddata.childsession
+            csession = self.childsession
         except AttributeError:
             if isinstance(self, (ChildSession, InternalSession)):
                 csession = self
             else:
                 csession = ChildSession(self)
-            self._threaddata.childsession = csession
+            self.childsession = csession
         # need shared pool set
         self.set_pool(checkclosed=False)
         return csession
@@ -443,11 +443,24 @@
         rset = self._execute(self, rql, kwargs, eid_key, build_descr)
         return self.decorate_rset(rset, propagate)
 
+    def _clear_thread_data(self):
+        """remove everything from the thread local storage, except pool
+        which is explicitly removed by reset_pool, and mode which is set anyway
+        by _touch
+        """
+        store = self._threaddata
+        for name in ('commit_state', 'transaction_data', 'pending_operations',
+                     '_rewriter'):
+            try:
+                delattr(store, name)
+            except AttributeError:
+                pass
+
     def commit(self, reset_pool=True):
         """commit the current session's transaction"""
         if self.pool is None:
             assert not self.pending_operations
-            self.transaction_data.clear()
+            self._clear_thread_data()
             self._touch()
             self.debug('commit session %s done (no db activity)', self.id)
             return
@@ -501,10 +514,8 @@
                                   exc_info=sys.exc_info())
             self.info('%s session %s done', trstate, self.id)
         finally:
+            self._clear_thread_data()
             self._touch()
-            self.commit_state = None
-            self.pending_operations[:] = []
-            self.transaction_data.clear()
             if reset_pool:
                 self.reset_pool(ignoremode=True)
 
@@ -512,7 +523,7 @@
         """rollback the current session's transaction"""
         if self.pool is None:
             assert not self.pending_operations
-            self.transaction_data.clear()
+            self._clear_thread_data()
             self._touch()
             self.debug('rollback session %s done (no db activity)', self.id)
             return
@@ -527,9 +538,8 @@
             self.pool.rollback()
             self.debug('rollback for session %s done', self.id)
         finally:
+            self._clear_thread_data()
             self._touch()
-            self.pending_operations[:] = []
-            self.transaction_data.clear()
             if reset_pool:
                 self.reset_pool(ignoremode=True)
 
@@ -584,6 +594,7 @@
 
     @property
     def rql_rewriter(self):
+        # in thread local storage since the rewriter isn't thread safe
         try:
             return self._threaddata._rewriter
         except AttributeError: