[Python-checkins] r76174 - in python/branches/release26-maint: Lib/test/lock_tests.py Lib/test/test_thread.py Lib/test/test_threading.py Lib/threading.py Misc/NEWS

antoine.pitrou python-checkins at python.org
Mon Nov 9 17:47:50 CET 2009


Author: antoine.pitrou
Date: Mon Nov  9 17:47:50 2009
New Revision: 76174

Log:
Merged revisions 76137,76172 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r76137 | antoine.pitrou | 2009-11-06 23:34:35 +0100 (ven., 06 nov. 2009) | 4 lines
  
  Issue #7270: Add some dedicated unit tests for multi-thread synchronization
  primitives such as Lock, RLock, Condition, Event and Semaphore.
........
  r76172 | antoine.pitrou | 2009-11-09 17:00:11 +0100 (lun., 09 nov. 2009) | 5 lines
  
  Issue #7282: Fix a memory leak when an RLock was used in a thread other
  than those started through `threading.Thread` (for example, using
  `thread.start_new_thread()`.
........


Added:
   python/branches/release26-maint/Lib/test/lock_tests.py
      - copied, changed from r76137, /python/trunk/Lib/test/lock_tests.py
Modified:
   python/branches/release26-maint/   (props changed)
   python/branches/release26-maint/Lib/test/test_thread.py
   python/branches/release26-maint/Lib/test/test_threading.py
   python/branches/release26-maint/Lib/threading.py
   python/branches/release26-maint/Misc/NEWS

Copied: python/branches/release26-maint/Lib/test/lock_tests.py (from r76137, /python/trunk/Lib/test/lock_tests.py)
==============================================================================
--- /python/trunk/Lib/test/lock_tests.py	(original)
+++ python/branches/release26-maint/Lib/test/lock_tests.py	Mon Nov  9 17:47:50 2009
@@ -130,6 +130,19 @@
         # Check the lock is unacquired
         Bunch(f, 1).wait_for_finished()
 
+    def test_thread_leak(self):
+        # The lock shouldn't leak a Thread instance when used from a foreign
+        # (non-threading) thread.
+        lock = self.locktype()
+        def f():
+            lock.acquire()
+            lock.release()
+        n = len(threading.enumerate())
+        # We run many threads in the hope that existing threads ids won't
+        # be recycled.
+        Bunch(f, 15).wait_for_finished()
+        self.assertEqual(n, len(threading.enumerate()))
+
 
 class LockTests(BaseLockTests):
     """
@@ -244,8 +257,10 @@
         results1 = []
         results2 = []
         def f():
-            results1.append(evt.wait())
-            results2.append(evt.wait())
+            evt.wait()
+            results1.append(evt.is_set())
+            evt.wait()
+            results2.append(evt.is_set())
         b = Bunch(f, N)
         b.wait_for_started()
         _wait()
@@ -269,9 +284,11 @@
         results2 = []
         N = 5
         def f():
-            results1.append(evt.wait(0.0))
+            evt.wait(0.0)
+            results1.append(evt.is_set())
             t1 = time.time()
-            r = evt.wait(0.2)
+            evt.wait(0.2)
+            r = evt.is_set()
             t2 = time.time()
             results2.append((r, t2 - t1))
         Bunch(f, N).wait_for_finished()

Modified: python/branches/release26-maint/Lib/test/test_thread.py
==============================================================================
--- python/branches/release26-maint/Lib/test/test_thread.py	(original)
+++ python/branches/release26-maint/Lib/test/test_thread.py	Mon Nov  9 17:47:50 2009
@@ -5,6 +5,7 @@
 import thread
 import time
 
+from test import lock_tests
 
 NUMTASKS = 10
 NUMTRIPS = 3
@@ -164,8 +165,12 @@
             self.done_mutex.release()
 
 
+class LockTests(lock_tests.LockTests):
+    locktype = thread.allocate_lock
+
+
 def test_main():
-    test_support.run_unittest(ThreadRunningTests, BarrierTest)
+    test_support.run_unittest(ThreadRunningTests, BarrierTest, LockTests)
 
 if __name__ == "__main__":
     test_main()

Modified: python/branches/release26-maint/Lib/test/test_threading.py
==============================================================================
--- python/branches/release26-maint/Lib/test/test_threading.py	(original)
+++ python/branches/release26-maint/Lib/test/test_threading.py	Mon Nov  9 17:47:50 2009
@@ -11,6 +11,8 @@
 import unittest
 import weakref
 
+from test import lock_tests
+
 # A trivial mutable counter.
 class Counter(object):
     def __init__(self):
@@ -132,11 +134,9 @@
     def test_foreign_thread(self):
         # Check that a "foreign" thread can use the threading module.
         def f(mutex):
-            # Acquiring an RLock forces an entry for the foreign
+            # Calling current_thread() forces an entry for the foreign
             # thread to get made in the threading._active map.
-            r = threading.RLock()
-            r.acquire()
-            r.release()
+            threading.current_thread()
             mutex.release()
 
         mutex = threading.Lock()
@@ -453,22 +453,6 @@
         thread.start()
         self.assertRaises(RuntimeError, thread.start)
 
-    def test_releasing_unacquired_rlock(self):
-        rlock = threading.RLock()
-        self.assertRaises(RuntimeError, rlock.release)
-
-    def test_waiting_on_unacquired_condition(self):
-        cond = threading.Condition()
-        self.assertRaises(RuntimeError, cond.wait)
-
-    def test_notify_on_unacquired_condition(self):
-        cond = threading.Condition()
-        self.assertRaises(RuntimeError, cond.notify)
-
-    def test_semaphore_with_negative_value(self):
-        self.assertRaises(ValueError, threading.Semaphore, value = -1)
-        self.assertRaises(ValueError, threading.Semaphore, value = -sys.maxint)
-
     def test_joining_current_thread(self):
         current_thread = threading.current_thread()
         self.assertRaises(RuntimeError, current_thread.join);
@@ -483,8 +467,34 @@
         self.assertRaises(RuntimeError, setattr, thread, "daemon", True)
 
 
+class LockTests(lock_tests.LockTests):
+    locktype = staticmethod(threading.Lock)
+
+class RLockTests(lock_tests.RLockTests):
+    locktype = staticmethod(threading.RLock)
+
+class EventTests(lock_tests.EventTests):
+    eventtype = staticmethod(threading.Event)
+
+class ConditionAsRLockTests(lock_tests.RLockTests):
+    # An Condition uses an RLock by default and exports its API.
+    locktype = staticmethod(threading.Condition)
+
+class ConditionTests(lock_tests.ConditionTests):
+    condtype = staticmethod(threading.Condition)
+
+class SemaphoreTests(lock_tests.SemaphoreTests):
+    semtype = staticmethod(threading.Semaphore)
+
+class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
+    semtype = staticmethod(threading.BoundedSemaphore)
+
+
 def test_main():
-    test.test_support.run_unittest(ThreadTests,
+    test.test_support.run_unittest(LockTests, RLockTests, EventTests,
+                                   ConditionAsRLockTests, ConditionTests,
+                                   SemaphoreTests, BoundedSemaphoreTests,
+                                   ThreadTests,
                                    ThreadJoinOnShutdown,
                                    ThreadingExceptionTests,
                                    )

Modified: python/branches/release26-maint/Lib/threading.py
==============================================================================
--- python/branches/release26-maint/Lib/threading.py	(original)
+++ python/branches/release26-maint/Lib/threading.py	Mon Nov  9 17:47:50 2009
@@ -106,14 +106,16 @@
 
     def __repr__(self):
         owner = self.__owner
-        return "<%s(%s, %d)>" % (
-                self.__class__.__name__,
-                owner and owner.name,
-                self.__count)
+        try:
+            owner = _active[owner].name
+        except KeyError:
+            pass
+        return "<%s owner=%r count=%d>" % (
+                self.__class__.__name__, owner, self.__count)
 
     def acquire(self, blocking=1):
-        me = current_thread()
-        if self.__owner is me:
+        me = _get_ident()
+        if self.__owner == me:
             self.__count = self.__count + 1
             if __debug__:
                 self._note("%s.acquire(%s): recursive success", self, blocking)
@@ -132,7 +134,7 @@
     __enter__ = acquire
 
     def release(self):
-        if self.__owner is not current_thread():
+        if self.__owner != _get_ident():
             raise RuntimeError("cannot release un-acquired lock")
         self.__count = count = self.__count - 1
         if not count:
@@ -168,7 +170,7 @@
         return (count, owner)
 
     def _is_owned(self):
-        return self.__owner is current_thread()
+        return self.__owner == _get_ident()
 
 
 def Condition(*args, **kwargs):

Modified: python/branches/release26-maint/Misc/NEWS
==============================================================================
--- python/branches/release26-maint/Misc/NEWS	(original)
+++ python/branches/release26-maint/Misc/NEWS	Mon Nov  9 17:47:50 2009
@@ -24,6 +24,10 @@
 Library
 -------
 
+- Issue #7282: Fix a memory leak when an RLock was used in a thread other
+  than those started through `threading.Thread` (for example, using
+  `thread.start_new_thread()`.
+
 - Issue #7264: Fix a possible deadlock when deallocating thread-local objects
   which are part of a reference cycle.
 
@@ -71,6 +75,9 @@
 Tests
 -----
 
+- Issue #7270: Add some dedicated unit tests for multi-thread synchronization
+  primitives such as Lock, RLock, Condition, Event and Semaphore.
+
 - Issue #7055: test___all__ now greedily detects all modules which have an
   __all__ attribute, rather than using a hardcoded and incomplete list.
 


More information about the Python-checkins mailing list