10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Very rudimentary test of threading module 20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport test.test_support 40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom test.test_support import verbose 50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom test.script_helper import assert_python_ok 60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport random 80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport re 90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport sys 100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yithread = test.test_support.import_module('thread') 110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yithreading = test.test_support.import_module('threading') 120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport time 130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport unittest 140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport weakref 150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport os 160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport subprocess 170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom test import lock_tests 190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# A trivial mutable counter. 210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass Counter(object): 220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __init__(self): 230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.value = 0 240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def inc(self): 250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.value += 1 260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def dec(self): 270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.value -= 1 280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def get(self): 290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self.value 300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass TestThread(threading.Thread): 320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __init__(self, name, testcase, sema, mutex, nrunning): 330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.Thread.__init__(self, name=name) 340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.testcase = testcase 350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.sema = sema 360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.mutex = mutex 370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.nrunning = nrunning 380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def run(self): 400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi delay = random.random() / 10000.0 410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'task %s will run for %.1f usec' % ( 430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.name, delay * 1e6) 440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi with self.sema: 460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi with self.mutex: 470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.nrunning.inc() 480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print self.nrunning.get(), 'tasks are running' 500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.testcase.assertTrue(self.nrunning.get() <= 3) 510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(delay) 530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'task', self.name, 'done' 550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi with self.mutex: 570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.nrunning.dec() 580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.testcase.assertTrue(self.nrunning.get() >= 0) 590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print '%s is finished. %d tasks are running' % ( 610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.name, self.nrunning.get()) 620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass BaseTestCase(unittest.TestCase): 640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def setUp(self): 650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._threads = test.test_support.threading_setup() 660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def tearDown(self): 680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi test.test_support.threading_cleanup(*self._threads) 690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi test.test_support.reap_children() 700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass ThreadTests(BaseTestCase): 730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Create a bunch of threads, let each do some work, wait until all are 750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # done. 760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_various_ops(self): 770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This takes about n/3 seconds to run (about n/3 clumps of tasks, 780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # times about 1 second per clump). 790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi NUMTASKS = 10 800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # no more than 3 of the 10 can run at once 820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sema = threading.BoundedSemaphore(value=3) 830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mutex = threading.RLock() 840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi numrunning = Counter() 850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threads = [] 870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for i in range(NUMTASKS): 890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = TestThread("<thread %d>"%i, self, sema, mutex, numrunning) 900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threads.append(t) 910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(t.ident, None) 920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(re.match('<TestThread\(.*, initial\)>', repr(t))) 930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'waiting for all tasks to complete' 970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for t in threads: 980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.join(NUMTASKS) 990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(not t.is_alive()) 1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertNotEqual(t.ident, 0) 1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertFalse(t.ident is None) 1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(re.match('<TestThread\(.*, \w+ -?\d+\)>', repr(t))) 1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'all tasks done' 1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(numrunning.get(), 0) 1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_ident_of_no_threading_threads(self): 1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # The ident still must work for the main thread and dummy threads. 1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertFalse(threading.currentThread().ident is None) 1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def f(): 1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ident.append(threading.currentThread().ident) 1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi done.set() 1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi done = threading.Event() 1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ident = [] 1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread.start_new_thread(f, ()) 1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi done.wait() 1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertFalse(ident[0] is None) 1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Kill the "immortal" _DummyThread 1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi del threading._active[ident[0]] 1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # run with a small(ish) thread stack size (256kB) 1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_various_ops_small_stack(self): 1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'with 256kB thread stack size...' 1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.stack_size(262144) 1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except thread.error: 1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'platform does not support changing thread stack size' 1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return 1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.test_various_ops() 1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.stack_size(0) 1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # run with a large thread stack size (1MB) 1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_various_ops_large_stack(self): 1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'with 1MB thread stack size...' 1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.stack_size(0x100000) 1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except thread.error: 1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'platform does not support changing thread stack size' 1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return 1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.test_various_ops() 1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.stack_size(0) 1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_foreign_thread(self): 1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Check that a "foreign" thread can use the threading module. 1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def f(mutex): 1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Calling current_thread() forces an entry for the foreign 1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # thread to get made in the threading._active map. 1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.current_thread() 1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mutex.release() 1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mutex = threading.Lock() 1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mutex.acquire() 1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi tid = thread.start_new_thread(f, (mutex,)) 1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Wait for the thread to finish. 1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mutex.acquire() 1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertIn(tid, threading._active) 1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertIsInstance(threading._active[tid], threading._DummyThread) 1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi del threading._active[tid] 1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # PyThreadState_SetAsyncExc() is a CPython-only gimmick, not (currently) 1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # exposed at the Python level. This test relies on ctypes to get at it. 1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_PyThreadState_SetAsyncExc(self): 1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import ctypes 1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except ImportError: 1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print "test_PyThreadState_SetAsyncExc can't import ctypes" 1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return # can't do anything 1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi set_async_exc = ctypes.pythonapi.PyThreadState_SetAsyncExc 1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi class AsyncExc(Exception): 1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi exception = ctypes.py_object(AsyncExc) 1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # First check it works when setting the exception from the same thread. 1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi tid = thread.get_ident() 1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi result = set_async_exc(ctypes.c_long(tid), exception) 1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # The exception is async, so we might have to keep the VM busy until 1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # it notices. 1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while True: 1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except AsyncExc: 1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This code is unreachable but it reflects the intent. If we wanted 1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # to be smarter the above loop wouldn't be infinite. 1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.fail("AsyncExc not raised") 1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(result, 1) # one thread state modified 1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except UnboundLocalError: 1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # The exception was raised too quickly for us to get the result. 2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # `worker_started` is set by the thread when it's inside a try/except 2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # block waiting to catch the asynchronously set AsyncExc exception. 2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # `worker_saw_exception` is set by the thread upon catching that 2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # exception. 2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi worker_started = threading.Event() 2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi worker_saw_exception = threading.Event() 2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi class Worker(threading.Thread): 2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def run(self): 2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.id = thread.get_ident() 2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.finished = False 2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while True: 2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi worker_started.set() 2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(0.1) 2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except AsyncExc: 2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.finished = True 2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi worker_saw_exception.set() 2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = Worker() 2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.daemon = True # so if this fails, we don't hang Python at shutdown 2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " started worker thread" 2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Try a thread id that doesn't make sense. 2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " trying nonsensical thread id" 2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi result = set_async_exc(ctypes.c_long(-1), exception) 2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(result, 0) # no thread states modified 2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Now raise an exception in the worker thread. 2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " waiting for worker thread to get started" 2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ret = worker_started.wait() 2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(ret) 2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " verifying worker hasn't exited" 2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(not t.finished) 2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " attempting to raise asynch exception in worker" 2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi result = set_async_exc(ctypes.c_long(t.id), exception) 2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(result, 1) # one thread state modified 2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " waiting for worker to say it caught the exception" 2480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi worker_saw_exception.wait(timeout=10) 2490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(t.finished) 2500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print " all OK -- joining worker" 2520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if t.finished: 2530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.join() 2540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # else the thread is still running, and we have no way to kill it 2550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_limbo_cleanup(self): 2570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue 7481: Failure to start thread should cleanup the limbo map. 2580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def fail_new_thread(*args): 2590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise thread.error() 2600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi _start_new_thread = threading._start_new_thread 2610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading._start_new_thread = fail_new_thread 2620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=lambda: None) 2640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertRaises(thread.error, t.start) 2650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertFalse( 2660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t in threading._limbo, 2670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi "Failed to cleanup _limbo map on failure of Thread.start().") 2680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finally: 2690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading._start_new_thread = _start_new_thread 2700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_finalize_runnning_thread(self): 2720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue 1402: the PyGILState_Ensure / _Release functions may be called 2730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # very late on python exit: on deallocation of a running thread for 2740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # example. 2750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import ctypes 2770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except ImportError: 2780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if verbose: 2790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print("test_finalize_with_runnning_thread can't import ctypes") 2800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return # can't do anything 2810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi rc = subprocess.call([sys.executable, "-c", """if 1: 2830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import ctypes, sys, time, thread 2840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This lock is used as a simple event variable. 2860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ready = thread.allocate_lock() 2870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ready.acquire() 2880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Module globals are cleared before __del__ is run 2900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # So we save the functions in class dict 2910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi class C: 2920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ensure = ctypes.pythonapi.PyGILState_Ensure 2930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi release = ctypes.pythonapi.PyGILState_Release 2940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __del__(self): 2950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi state = self.ensure() 2960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.release(state) 2970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def waitingThread(): 2990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi x = C() 3000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ready.release() 3010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(100) 3020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread.start_new_thread(waitingThread, ()) 3040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ready.acquire() # Be sure the other thread is waiting. 3050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.exit(42) 3060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """]) 3070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(rc, 42) 3080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_finalize_with_trace(self): 3100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue1733757 3110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Avoid a deadlock when sys.settrace steps into threading._shutdown 3120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi p = subprocess.Popen([sys.executable, "-c", """if 1: 3130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import sys, threading 3140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # A deadlock-killer, to prevent the 3160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # testsuite to hang forever 3170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def killer(): 3180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import os, time 3190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(2) 3200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'program blocked; aborting' 3210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os._exit(2) 3220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=killer) 3230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.daemon = True 3240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 3250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This is the trace function 3270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def func(frame, event, arg): 3280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.current_thread() 3290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return func 3300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.settrace(func) 3320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """], 3330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout=subprocess.PIPE, 3340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stderr=subprocess.PIPE) 3350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.addCleanup(p.stdout.close) 3360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.addCleanup(p.stderr.close) 3370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout, stderr = p.communicate() 3380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi rc = p.returncode 3390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertFalse(rc == 2, "interpreted was blocked") 3400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(rc == 0, 3410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi "Unexpected error: " + repr(stderr)) 3420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_join_nondaemon_on_shutdown(self): 3440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue 1722344 3450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Raising SystemExit skipped threading._shutdown 3460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi p = subprocess.Popen([sys.executable, "-c", """if 1: 3470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import threading 3480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi from time import sleep 3490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def child(): 3510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sleep(1) 3520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # As a non-daemon thread we SHOULD wake up and nothing 3530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # should be torn down yet 3540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print "Woke up, sleep function is:", sleep 3550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.Thread(target=child).start() 3570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise SystemExit 3580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """], 3590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout=subprocess.PIPE, 3600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stderr=subprocess.PIPE) 3610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.addCleanup(p.stdout.close) 3620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.addCleanup(p.stderr.close) 3630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout, stderr = p.communicate() 3640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(stdout.strip(), 3650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi "Woke up, sleep function is: <built-in function sleep>") 3660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stderr = re.sub(r"^\[\d+ refs\]", "", stderr, re.MULTILINE).strip() 3670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(stderr, "") 3680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_enumerate_after_join(self): 3700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Try hard to trigger #1703448: a thread is still returned in 3710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # threading.enumerate() after it has been join()ed. 3720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi enum = threading.enumerate 3730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi old_interval = sys.getcheckinterval() 3740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 3750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for i in xrange(1, 100): 3760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Try a couple times at each thread-switching interval 3770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # to get more interleavings. 3780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.setcheckinterval(i // 5) 3790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=lambda: None) 3800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 3810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.join() 3820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi l = enum() 3830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertNotIn(t, l, 3840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi "#1703448 triggered after %d trials: %s" % (i, l)) 3850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finally: 3860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.setcheckinterval(old_interval) 3870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_no_refcycle_through_target(self): 3890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi class RunSelfFunction(object): 3900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __init__(self, should_raise): 3910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # The links in this refcycle from Thread back to self 3920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # should be cleaned up when the thread completes. 3930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.should_raise = should_raise 3940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.thread = threading.Thread(target=self._run, 3950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi args=(self,), 3960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi kwargs={'yet_another':self}) 3970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.thread.start() 3980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 3990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def _run(self, other_ref, yet_another): 4000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if self.should_raise: 4010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise SystemExit 4020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi cyclic_object = RunSelfFunction(should_raise=False) 4040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi weak_cyclic_object = weakref.ref(cyclic_object) 4050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi cyclic_object.thread.join() 4060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi del cyclic_object 4070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(None, weak_cyclic_object(), 4080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi msg=('%d references still around' % 4090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.getrefcount(weak_cyclic_object()))) 4100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raising_cyclic_object = RunSelfFunction(should_raise=True) 4120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi weak_raising_cyclic_object = weakref.ref(raising_cyclic_object) 4130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raising_cyclic_object.thread.join() 4140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi del raising_cyclic_object 4150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(None, weak_raising_cyclic_object(), 4160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi msg=('%d references still around' % 4170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.getrefcount(weak_raising_cyclic_object()))) 4180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()') 4200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_dummy_thread_after_fork(self): 4210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue #14308: a dummy thread in the active list doesn't mess up 4220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # the after-fork mechanism. 4230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi code = """if 1: 4240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import thread, threading, os, time 4250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def background_thread(evt): 4270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Creates and registers the _DummyThread instance 4280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threading.current_thread() 4290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi evt.set() 4300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(10) 4310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi evt = threading.Event() 4330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread.start_new_thread(background_thread, (evt,)) 4340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi evt.wait() 4350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi assert threading.active_count() == 2, threading.active_count() 4360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if os.fork() == 0: 4370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi assert threading.active_count() == 1, threading.active_count() 4380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os._exit(0) 4390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 4400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os.wait() 4410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 4420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi _, out, err = assert_python_ok("-c", code) 4430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(out, '') 4440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(err, '') 4450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass ThreadJoinOnShutdown(BaseTestCase): 4480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Between fork() and exec(), only async-safe functions are allowed (issues 4500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # #12316 and #11870), and fork() from a worker thread is known to trigger 4510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # problems with some operating systems (issue #3863): skip problematic tests 4520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # on platforms known to behave badly. 4530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi platforms_to_skip = ('freebsd4', 'freebsd5', 'freebsd6', 'netbsd5', 4540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 'os2emx') 4550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def _run_and_join(self, script): 4570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if 1: 4580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import sys, os, time, threading 4590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # a thread, which waits for the main program to terminate 4610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def joiningfunc(mainthread): 4620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mainthread.join() 4630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'end of thread' 4640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi \n""" + script 4650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi p = subprocess.Popen([sys.executable, "-c", script], stdout=subprocess.PIPE) 4670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi rc = p.wait() 4680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi data = p.stdout.read().replace('\r', '') 4690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi p.stdout.close() 4700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(data, "end of main\nend of thread\n") 4710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertFalse(rc == 2, "interpreter was blocked") 4720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertTrue(rc == 0, "Unexpected error") 4730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_1_join_on_shutdown(self): 4750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # The usual case: on exit, wait for a non-daemon thread 4760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if 1: 4770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import os 4780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=joiningfunc, 4790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi args=(threading.current_thread(),)) 4800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 4810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(0.1) 4820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'end of main' 4830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 4840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._run_and_join(script) 4850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") 4880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") 4890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_2_join_in_forked_process(self): 4900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Like the test above, but from a forked interpreter 4910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if 1: 4920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi childpid = os.fork() 4930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if childpid != 0: 4940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os.waitpid(childpid, 0) 4950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.exit(0) 4960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=joiningfunc, 4980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi args=(threading.current_thread(),)) 4990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 5000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'end of main' 5010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 5020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._run_and_join(script) 5030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") 5050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") 5060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_3_join_in_forked_from_thread(self): 5070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Like the test above, but fork() was called from a worker thread 5080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # In the forked process, the main Thread object must be marked as stopped. 5090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if 1: 5100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi main_thread = threading.current_thread() 5110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def worker(): 5120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi childpid = os.fork() 5130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if childpid != 0: 5140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os.waitpid(childpid, 0) 5150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sys.exit(0) 5160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=joiningfunc, 5180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi args=(main_thread,)) 5190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print 'end of main' 5200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 5210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.join() # Should not block: main_thread is already stopped 5220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w = threading.Thread(target=worker) 5240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.start() 5250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 5260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._run_and_join(script) 5270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def assertScriptHasOutput(self, script, expected_output): 5290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi p = subprocess.Popen([sys.executable, "-c", script], 5300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout=subprocess.PIPE) 5310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi rc = p.wait() 5320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi data = p.stdout.read().decode().replace('\r', '') 5330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(rc, 0, "Unexpected error") 5340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(data, expected_output) 5350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") 5370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") 5380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_4_joining_across_fork_in_worker_thread(self): 5390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # There used to be a possible deadlock when forking from a child 5400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # thread. See http://bugs.python.org/issue6643. 5410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # The script takes the following steps: 5430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # - The main thread in the parent process starts a new thread and then 5440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # tries to join it. 5450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # - The join operation acquires the Lock inside the thread's _block 5460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Condition. (See threading.py:Thread.join().) 5470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # - We stub out the acquire method on the condition to force it to wait 5480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # until the child thread forks. (See LOCK ACQUIRED HERE) 5490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # - The child thread forks. (See LOCK HELD and WORKER THREAD FORKS 5500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # HERE) 5510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # - The main thread of the parent process enters Condition.wait(), 5520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # which releases the lock on the child thread. 5530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # - The child process returns. Without the necessary fix, when the 5540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # main thread of the child process (which used to be the child thread 5550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # in the parent process) attempts to exit, it will try to acquire the 5560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # lock in the Thread._block Condition object and hang, because the 5570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # lock was held across the fork. 5580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if 1: 5600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import os, time, threading 5610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finish_join = False 5630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi start_fork = False 5640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def worker(): 5660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Wait until this thread's lock is acquired before forking to 5670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # create the deadlock. 5680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi global finish_join 5690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while not start_fork: 5700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(0.01) 5710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # LOCK HELD: Main thread holds lock across this call. 5720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi childpid = os.fork() 5730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finish_join = True 5740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if childpid != 0: 5750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Parent process just waits for child. 5760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os.waitpid(childpid, 0) 5770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Child process should just return. 5780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w = threading.Thread(target=worker) 5800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 5810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Stub out the private condition variable's lock acquire method. 5820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This acquires the lock and then waits until the child has forked 5830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # before returning, which will release the lock soon after. If 5840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # someone else tries to fix this test case by acquiring this lock 5850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # before forking instead of resetting it, the test case will 5860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # deadlock when it shouldn't. 5870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi condition = w._block 5880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi orig_acquire = condition.acquire 5890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi call_count_lock = threading.Lock() 5900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi call_count = 0 5910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def my_acquire(): 5920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi global call_count 5930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi global start_fork 5940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi orig_acquire() # LOCK ACQUIRED HERE 5950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi start_fork = True 5960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if call_count == 0: 5970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while not finish_join: 5980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(0.01) # WORKER THREAD FORKS HERE 5990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi with call_count_lock: 6000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi call_count += 1 6010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi condition.acquire = my_acquire 6020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.start() 6040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.join() 6050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print('end of main') 6060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 6070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertScriptHasOutput(script, "end of main\n") 6080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") 6100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") 6110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_5_clear_waiter_locks_to_avoid_crash(self): 6120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Check that a spawned thread that forks doesn't segfault on certain 6130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # platforms, namely OS X. This used to happen if there was a waiter 6140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # lock in the thread's condition variable's waiters list. Even though 6150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # we know the lock will be held across the fork, it is not safe to 6160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # release locks held across forks on all platforms, so releasing the 6170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # waiter lock caused a segfault on OS X. Furthermore, since locks on 6180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # OS X are (as of this writing) implemented with a mutex + condition 6190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # variable instead of a semaphore, while we know that the Python-level 6200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # lock will be acquired, we can't know if the internal mutex will be 6210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # acquired at the time of the fork. 6220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if True: 6240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import os, time, threading 6250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi start_fork = False 6270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def worker(): 6290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Wait until the main thread has attempted to join this thread 6300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # before continuing. 6310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while not start_fork: 6320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi time.sleep(0.01) 6330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi childpid = os.fork() 6340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if childpid != 0: 6350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Parent process just waits for child. 6360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi (cpid, rc) = os.waitpid(childpid, 0) 6370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi assert cpid == childpid 6380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi assert rc == 0 6390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print('end of worker thread') 6400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 6410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Child process should just return. 6420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 6430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w = threading.Thread(target=worker) 6450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Stub out the private condition variable's _release_save method. 6470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This releases the condition's lock and flips the global that 6480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # causes the worker to fork. At this point, the problematic waiter 6490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # lock has been acquired once by the waiter and has been put onto 6500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # the waiters list. 6510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi condition = w._block 6520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi orig_release_save = condition._release_save 6530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def my_release_save(): 6540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi global start_fork 6550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi orig_release_save() 6560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Waiter lock held here, condition lock released. 6570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi start_fork = True 6580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi condition._release_save = my_release_save 6590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.start() 6610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.join() 6620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print('end of main thread') 6630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 6640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi output = "end of worker thread\nend of main thread\n" 6650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertScriptHasOutput(script, output) 6660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") 6680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") 6690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_reinit_tls_after_fork(self): 6700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue #13817: fork() would deadlock in a multithreaded program with 6710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # the ad-hoc TLS implementation. 6720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def do_fork_and_wait(): 6740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # just fork a child process and wait it 6750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pid = os.fork() 6760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if pid > 0: 6770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os.waitpid(pid, 0) 6780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 6790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi os._exit(0) 6800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # start a bunch of threads that will fork() child processes 6820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threads = [] 6830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for i in range(16): 6840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = threading.Thread(target=do_fork_and_wait) 6850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threads.append(t) 6860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.start() 6870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for t in threads: 6890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t.join() 6900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 6920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass ThreadingExceptionTests(BaseTestCase): 6930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # A RuntimeError should be raised if Thread.start() is called 6940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # multiple times. 6950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_start_thread_again(self): 6960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread = threading.Thread() 6970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread.start() 6980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertRaises(RuntimeError, thread.start) 6990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_joining_current_thread(self): 7010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi current_thread = threading.current_thread() 7020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertRaises(RuntimeError, current_thread.join); 7030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_joining_inactive_thread(self): 7050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread = threading.Thread() 7060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertRaises(RuntimeError, thread.join) 7070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_daemonize_active_thread(self): 7090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread = threading.Thread() 7100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi thread.start() 7110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertRaises(RuntimeError, setattr, thread, "daemon", True) 7120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass LockTests(lock_tests.LockTests): 7150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi locktype = staticmethod(threading.Lock) 7160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass RLockTests(lock_tests.RLockTests): 7180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi locktype = staticmethod(threading.RLock) 7190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass EventTests(lock_tests.EventTests): 7210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi eventtype = staticmethod(threading.Event) 7220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass ConditionAsRLockTests(lock_tests.RLockTests): 7240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # An Condition uses an RLock by default and exports its API. 7250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi locktype = staticmethod(threading.Condition) 7260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass ConditionTests(lock_tests.ConditionTests): 7280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi condtype = staticmethod(threading.Condition) 7290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass SemaphoreTests(lock_tests.SemaphoreTests): 7310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi semtype = staticmethod(threading.Semaphore) 7320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests): 7340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi semtype = staticmethod(threading.BoundedSemaphore) 7350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi @unittest.skipUnless(sys.platform == 'darwin', 'test macosx problem') 7370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def test_recursion_limit(self): 7380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Issue 9670 7390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # test that excessive recursion within a non-main thread causes 7400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # an exception rather than crashing the interpreter on platforms 7410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # like Mac OS X or FreeBSD which have small default stack sizes 7420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # for threads 7430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi script = """if True: 7440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import threading 7450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def recurse(): 7470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return recurse() 7480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def outer(): 7500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 7510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi recurse() 7520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except RuntimeError: 7530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 7540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w = threading.Thread(target=outer) 7560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.start() 7570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi w.join() 7580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print('end of main thread') 7590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 7600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi expected_output = "end of main thread\n" 7610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi p = subprocess.Popen([sys.executable, "-c", script], 7620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout=subprocess.PIPE) 7630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stdout, stderr = p.communicate() 7640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi data = stdout.decode().replace('\r', '') 7650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(p.returncode, 0, "Unexpected error") 7660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.assertEqual(data, expected_output) 7670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef test_main(): 7690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi test.test_support.run_unittest(LockTests, RLockTests, EventTests, 7700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ConditionAsRLockTests, ConditionTests, 7710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi SemaphoreTests, BoundedSemaphoreTests, 7720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ThreadTests, 7730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ThreadJoinOnShutdown, 7740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ThreadingExceptionTests, 7750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ) 7760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 7770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiif __name__ == "__main__": 7780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi test_main() 779