summaryrefslogtreecommitdiffstats
path: root/Lib/importlib/test
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2012-05-17 16:55:59 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2012-05-17 16:55:59 (GMT)
commitea3eb88bcaef775c8f2bbe310053f9990a8c9ea7 (patch)
tree9461ae28e833a95a79cc4811df68435bf0e00fc3 /Lib/importlib/test
parent5cec9d2ae57da6fb5c424bb297ddb67902393b2d (diff)
downloadcpython-ea3eb88bcaef775c8f2bbe310053f9990a8c9ea7.zip
cpython-ea3eb88bcaef775c8f2bbe310053f9990a8c9ea7.tar.gz
cpython-ea3eb88bcaef775c8f2bbe310053f9990a8c9ea7.tar.bz2
Issue #9260: A finer-grained import lock.
Most of the import sequence now uses per-module locks rather than the global import lock, eliminating well-known issues with threads and imports.
Diffstat (limited to 'Lib/importlib/test')
-rw-r--r--Lib/importlib/test/test_locks.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/Lib/importlib/test/test_locks.py b/Lib/importlib/test/test_locks.py
new file mode 100644
index 0000000..35a72d4
--- /dev/null
+++ b/Lib/importlib/test/test_locks.py
@@ -0,0 +1,115 @@
+from importlib import _bootstrap
+import time
+import unittest
+import weakref
+
+from test import support
+
+try:
+ import threading
+except ImportError:
+ threading = None
+else:
+ from test import lock_tests
+
+
+LockType = _bootstrap._ModuleLock
+DeadlockError = _bootstrap._DeadlockError
+
+
+if threading is not None:
+ class ModuleLockAsRLockTests(lock_tests.RLockTests):
+ locktype = staticmethod(lambda: LockType("some_lock"))
+
+ # _is_owned() unsupported
+ test__is_owned = None
+ # acquire(blocking=False) unsupported
+ test_try_acquire = None
+ test_try_acquire_contended = None
+ # `with` unsupported
+ test_with = None
+ # acquire(timeout=...) unsupported
+ test_timeout = None
+ # _release_save() unsupported
+ test_release_save_unacquired = None
+
+else:
+ class ModuleLockAsRLockTests(unittest.TestCase):
+ pass
+
+
+@unittest.skipUnless(threading, "threads needed for this test")
+class DeadlockAvoidanceTests(unittest.TestCase):
+
+ def run_deadlock_avoidance_test(self, create_deadlock):
+ NLOCKS = 10
+ locks = [LockType(str(i)) for i in range(NLOCKS)]
+ pairs = [(locks[i], locks[(i+1)%NLOCKS]) for i in range(NLOCKS)]
+ if create_deadlock:
+ NTHREADS = NLOCKS
+ else:
+ NTHREADS = NLOCKS - 1
+ barrier = threading.Barrier(NTHREADS)
+ results = []
+ def _acquire(lock):
+ """Try to acquire the lock. Return True on success, False on deadlock."""
+ try:
+ lock.acquire()
+ except DeadlockError:
+ return False
+ else:
+ return True
+ def f():
+ a, b = pairs.pop()
+ ra = _acquire(a)
+ barrier.wait()
+ rb = _acquire(b)
+ results.append((ra, rb))
+ if rb:
+ b.release()
+ if ra:
+ a.release()
+ lock_tests.Bunch(f, NTHREADS).wait_for_finished()
+ self.assertEqual(len(results), NTHREADS)
+ return results
+
+ def test_deadlock(self):
+ results = self.run_deadlock_avoidance_test(True)
+ # One of the threads detected a potential deadlock on its second
+ # acquire() call.
+ self.assertEqual(results.count((True, False)), 1)
+ self.assertEqual(results.count((True, True)), len(results) - 1)
+
+ def test_no_deadlock(self):
+ results = self.run_deadlock_avoidance_test(False)
+ self.assertEqual(results.count((True, False)), 0)
+ self.assertEqual(results.count((True, True)), len(results))
+
+
+class LifetimeTests(unittest.TestCase):
+
+ def test_lock_lifetime(self):
+ name = "xyzzy"
+ self.assertNotIn(name, _bootstrap._module_locks)
+ lock = _bootstrap._get_module_lock(name)
+ self.assertIn(name, _bootstrap._module_locks)
+ wr = weakref.ref(lock)
+ del lock
+ support.gc_collect()
+ self.assertNotIn(name, _bootstrap._module_locks)
+ self.assertIs(wr(), None)
+
+ def test_all_locks(self):
+ support.gc_collect()
+ self.assertEqual(0, len(_bootstrap._module_locks))
+
+
+@support.reap_threads
+def test_main():
+ support.run_unittest(ModuleLockAsRLockTests,
+ DeadlockAvoidanceTests,
+ LifetimeTests)
+
+
+if __name__ == '__main__':
+ test_main()