mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
Issue #21755: Skip {Frozen,Source}_DeadlockAvoidanceTests tests when
Python is built without threads.
This commit is contained in:
parent
748ff8bfd1
commit
f7eaa0c63c
1 changed files with 72 additions and 60 deletions
|
@ -52,74 +52,86 @@ else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(threading, "threads needed for this test")
|
if threading is not None:
|
||||||
class DeadlockAvoidanceTests:
|
class DeadlockAvoidanceTests:
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
try:
|
|
||||||
self.old_switchinterval = sys.getswitchinterval()
|
|
||||||
sys.setswitchinterval(0.000001)
|
|
||||||
except AttributeError:
|
|
||||||
self.old_switchinterval = None
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
if self.old_switchinterval is not None:
|
|
||||||
sys.setswitchinterval(self.old_switchinterval)
|
|
||||||
|
|
||||||
def run_deadlock_avoidance_test(self, create_deadlock):
|
|
||||||
NLOCKS = 10
|
|
||||||
locks = [self.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:
|
try:
|
||||||
lock.acquire()
|
self.old_switchinterval = sys.getswitchinterval()
|
||||||
except self.DeadlockError:
|
sys.setswitchinterval(0.000001)
|
||||||
return False
|
except AttributeError:
|
||||||
|
self.old_switchinterval = None
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if self.old_switchinterval is not None:
|
||||||
|
sys.setswitchinterval(self.old_switchinterval)
|
||||||
|
|
||||||
|
def run_deadlock_avoidance_test(self, create_deadlock):
|
||||||
|
NLOCKS = 10
|
||||||
|
locks = [self.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:
|
else:
|
||||||
return True
|
NTHREADS = NLOCKS - 1
|
||||||
def f():
|
barrier = threading.Barrier(NTHREADS)
|
||||||
a, b = pairs.pop()
|
results = []
|
||||||
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):
|
def _acquire(lock):
|
||||||
results = self.run_deadlock_avoidance_test(True)
|
"""Try to acquire the lock. Return True on success,
|
||||||
# At least one of the threads detected a potential deadlock on its
|
False on deadlock."""
|
||||||
# second acquire() call. It may be several of them, because the
|
try:
|
||||||
# deadlock avoidance mechanism is conservative.
|
lock.acquire()
|
||||||
nb_deadlocks = results.count((True, False))
|
except self.DeadlockError:
|
||||||
self.assertGreaterEqual(nb_deadlocks, 1)
|
return False
|
||||||
self.assertEqual(results.count((True, True)), len(results) - nb_deadlocks)
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
def test_no_deadlock(self):
|
def f():
|
||||||
results = self.run_deadlock_avoidance_test(False)
|
a, b = pairs.pop()
|
||||||
self.assertEqual(results.count((True, False)), 0)
|
ra = _acquire(a)
|
||||||
self.assertEqual(results.count((True, True)), len(results))
|
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)
|
||||||
|
# At least one of the threads detected a potential deadlock on its
|
||||||
|
# second acquire() call. It may be several of them, because the
|
||||||
|
# deadlock avoidance mechanism is conservative.
|
||||||
|
nb_deadlocks = results.count((True, False))
|
||||||
|
self.assertGreaterEqual(nb_deadlocks, 1)
|
||||||
|
self.assertEqual(results.count((True, True)), len(results) - nb_deadlocks)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
DEADLOCK_ERRORS = {kind: splitinit._bootstrap._DeadlockError
|
DEADLOCK_ERRORS = {kind: splitinit._bootstrap._DeadlockError
|
||||||
for kind, splitinit in init.items()}
|
for kind, splitinit in init.items()}
|
||||||
|
|
||||||
(Frozen_DeadlockAvoidanceTests,
|
(Frozen_DeadlockAvoidanceTests,
|
||||||
Source_DeadlockAvoidanceTests
|
Source_DeadlockAvoidanceTests
|
||||||
) = test_util.test_both(DeadlockAvoidanceTests,
|
) = test_util.test_both(DeadlockAvoidanceTests,
|
||||||
LockType=LOCK_TYPES, DeadlockError=DEADLOCK_ERRORS)
|
LockType=LOCK_TYPES,
|
||||||
|
DeadlockError=DEADLOCK_ERRORS)
|
||||||
|
else:
|
||||||
|
DEADLOCK_ERRORS = {}
|
||||||
|
|
||||||
|
class Frozen_DeadlockAvoidanceTests(unittest.TestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Source_DeadlockAvoidanceTests(unittest.TestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LifetimeTests:
|
class LifetimeTests:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue