PageRenderTime 84ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/lib-python/modified-2.7/test/test_threading.py

https://bitbucket.org/dac_io/pypy
Python | 740 lines | 660 code | 44 blank | 36 comment | 46 complexity | 086b27e72916f2a768241778065e1bd1 MD5 | raw file
  1. # Very rudimentary test of threading module
  2. import test.test_support
  3. from test.test_support import verbose
  4. import random
  5. import re
  6. import sys
  7. thread = test.test_support.import_module('thread')
  8. threading = test.test_support.import_module('threading')
  9. import time
  10. import unittest
  11. import weakref
  12. import os
  13. import subprocess
  14. from test import lock_tests
  15. # A trivial mutable counter.
  16. class Counter(object):
  17. def __init__(self):
  18. self.value = 0
  19. def inc(self):
  20. self.value += 1
  21. def dec(self):
  22. self.value -= 1
  23. def get(self):
  24. return self.value
  25. class TestThread(threading.Thread):
  26. def __init__(self, name, testcase, sema, mutex, nrunning):
  27. threading.Thread.__init__(self, name=name)
  28. self.testcase = testcase
  29. self.sema = sema
  30. self.mutex = mutex
  31. self.nrunning = nrunning
  32. def run(self):
  33. delay = random.random() / 10000.0
  34. if verbose:
  35. print 'task %s will run for %.1f usec' % (
  36. self.name, delay * 1e6)
  37. with self.sema:
  38. with self.mutex:
  39. self.nrunning.inc()
  40. if verbose:
  41. print self.nrunning.get(), 'tasks are running'
  42. self.testcase.assertTrue(self.nrunning.get() <= 3)
  43. time.sleep(delay)
  44. if verbose:
  45. print 'task', self.name, 'done'
  46. with self.mutex:
  47. self.nrunning.dec()
  48. self.testcase.assertTrue(self.nrunning.get() >= 0)
  49. if verbose:
  50. print '%s is finished. %d tasks are running' % (
  51. self.name, self.nrunning.get())
  52. class BaseTestCase(unittest.TestCase):
  53. def setUp(self):
  54. self._threads = test.test_support.threading_setup()
  55. def tearDown(self):
  56. test.test_support.threading_cleanup(*self._threads)
  57. test.test_support.reap_children()
  58. class ThreadTests(BaseTestCase):
  59. # Create a bunch of threads, let each do some work, wait until all are
  60. # done.
  61. def test_various_ops(self):
  62. # This takes about n/3 seconds to run (about n/3 clumps of tasks,
  63. # times about 1 second per clump).
  64. NUMTASKS = 10
  65. # no more than 3 of the 10 can run at once
  66. sema = threading.BoundedSemaphore(value=3)
  67. mutex = threading.RLock()
  68. numrunning = Counter()
  69. threads = []
  70. for i in range(NUMTASKS):
  71. t = TestThread("<thread %d>"%i, self, sema, mutex, numrunning)
  72. threads.append(t)
  73. self.assertEqual(t.ident, None)
  74. self.assertTrue(re.match('<TestThread\(.*, initial\)>', repr(t)))
  75. t.start()
  76. if verbose:
  77. print 'waiting for all tasks to complete'
  78. for t in threads:
  79. t.join(NUMTASKS)
  80. self.assertTrue(not t.is_alive())
  81. self.assertNotEqual(t.ident, 0)
  82. self.assertFalse(t.ident is None)
  83. self.assertTrue(re.match('<TestThread\(.*, \w+ -?\d+\)>', repr(t)))
  84. if verbose:
  85. print 'all tasks done'
  86. self.assertEqual(numrunning.get(), 0)
  87. def test_ident_of_no_threading_threads(self):
  88. # The ident still must work for the main thread and dummy threads.
  89. self.assertFalse(threading.currentThread().ident is None)
  90. def f():
  91. ident.append(threading.currentThread().ident)
  92. done.set()
  93. done = threading.Event()
  94. ident = []
  95. thread.start_new_thread(f, ())
  96. done.wait()
  97. self.assertFalse(ident[0] is None)
  98. # Kill the "immortal" _DummyThread
  99. del threading._active[ident[0]]
  100. # run with a small(ish) thread stack size (256kB)
  101. def test_various_ops_small_stack(self):
  102. if verbose:
  103. print 'with 256kB thread stack size...'
  104. try:
  105. threading.stack_size(262144)
  106. except thread.error:
  107. if verbose:
  108. print 'platform does not support changing thread stack size'
  109. return
  110. self.test_various_ops()
  111. threading.stack_size(0)
  112. # run with a large thread stack size (1MB)
  113. def test_various_ops_large_stack(self):
  114. if verbose:
  115. print 'with 1MB thread stack size...'
  116. try:
  117. threading.stack_size(0x100000)
  118. except thread.error:
  119. if verbose:
  120. print 'platform does not support changing thread stack size'
  121. return
  122. self.test_various_ops()
  123. threading.stack_size(0)
  124. def test_foreign_thread(self):
  125. # Check that a "foreign" thread can use the threading module.
  126. def f(mutex):
  127. # Calling current_thread() forces an entry for the foreign
  128. # thread to get made in the threading._active map.
  129. threading.current_thread()
  130. mutex.release()
  131. mutex = threading.Lock()
  132. mutex.acquire()
  133. tid = thread.start_new_thread(f, (mutex,))
  134. # Wait for the thread to finish.
  135. mutex.acquire()
  136. self.assertIn(tid, threading._active)
  137. self.assertIsInstance(threading._active[tid], threading._DummyThread)
  138. del threading._active[tid]
  139. # PyThreadState_SetAsyncExc() is a CPython-only gimmick, not (currently)
  140. # exposed at the Python level. This test relies on ctypes to get at it.
  141. @test.test_support.cpython_only
  142. def test_PyThreadState_SetAsyncExc(self):
  143. try:
  144. import ctypes
  145. except ImportError:
  146. if verbose:
  147. print "test_PyThreadState_SetAsyncExc can't import ctypes"
  148. return # can't do anything
  149. set_async_exc = ctypes.pythonapi.PyThreadState_SetAsyncExc
  150. class AsyncExc(Exception):
  151. pass
  152. exception = ctypes.py_object(AsyncExc)
  153. # First check it works when setting the exception from the same thread.
  154. tid = thread.get_ident()
  155. try:
  156. result = set_async_exc(ctypes.c_long(tid), exception)
  157. # The exception is async, so we might have to keep the VM busy until
  158. # it notices.
  159. while True:
  160. pass
  161. except AsyncExc:
  162. pass
  163. else:
  164. # This code is unreachable but it reflects the intent. If we wanted
  165. # to be smarter the above loop wouldn't be infinite.
  166. self.fail("AsyncExc not raised")
  167. try:
  168. self.assertEqual(result, 1) # one thread state modified
  169. except UnboundLocalError:
  170. # The exception was raised too quickly for us to get the result.
  171. pass
  172. # `worker_started` is set by the thread when it's inside a try/except
  173. # block waiting to catch the asynchronously set AsyncExc exception.
  174. # `worker_saw_exception` is set by the thread upon catching that
  175. # exception.
  176. worker_started = threading.Event()
  177. worker_saw_exception = threading.Event()
  178. class Worker(threading.Thread):
  179. def run(self):
  180. self.id = thread.get_ident()
  181. self.finished = False
  182. try:
  183. while True:
  184. worker_started.set()
  185. time.sleep(0.1)
  186. except AsyncExc:
  187. self.finished = True
  188. worker_saw_exception.set()
  189. t = Worker()
  190. t.daemon = True # so if this fails, we don't hang Python at shutdown
  191. t.start()
  192. if verbose:
  193. print " started worker thread"
  194. # Try a thread id that doesn't make sense.
  195. if verbose:
  196. print " trying nonsensical thread id"
  197. result = set_async_exc(ctypes.c_long(-1), exception)
  198. self.assertEqual(result, 0) # no thread states modified
  199. # Now raise an exception in the worker thread.
  200. if verbose:
  201. print " waiting for worker thread to get started"
  202. ret = worker_started.wait()
  203. self.assertTrue(ret)
  204. if verbose:
  205. print " verifying worker hasn't exited"
  206. self.assertTrue(not t.finished)
  207. if verbose:
  208. print " attempting to raise asynch exception in worker"
  209. result = set_async_exc(ctypes.c_long(t.id), exception)
  210. self.assertEqual(result, 1) # one thread state modified
  211. if verbose:
  212. print " waiting for worker to say it caught the exception"
  213. worker_saw_exception.wait(timeout=10)
  214. self.assertTrue(t.finished)
  215. if verbose:
  216. print " all OK -- joining worker"
  217. if t.finished:
  218. t.join()
  219. # else the thread is still running, and we have no way to kill it
  220. def test_limbo_cleanup(self):
  221. # Issue 7481: Failure to start thread should cleanup the limbo map.
  222. def fail_new_thread(*args):
  223. raise thread.error()
  224. _start_new_thread = threading._start_new_thread
  225. threading._start_new_thread = fail_new_thread
  226. try:
  227. t = threading.Thread(target=lambda: None)
  228. self.assertRaises(thread.error, t.start)
  229. self.assertFalse(
  230. t in threading._limbo,
  231. "Failed to cleanup _limbo map on failure of Thread.start().")
  232. finally:
  233. threading._start_new_thread = _start_new_thread
  234. @test.test_support.cpython_only
  235. def test_finalize_runnning_thread(self):
  236. # Issue 1402: the PyGILState_Ensure / _Release functions may be called
  237. # very late on python exit: on deallocation of a running thread for
  238. # example.
  239. try:
  240. import ctypes
  241. except ImportError:
  242. if verbose:
  243. print("test_finalize_with_runnning_thread can't import ctypes")
  244. return # can't do anything
  245. rc = subprocess.call([sys.executable, "-c", """if 1:
  246. import ctypes, sys, time, thread
  247. # This lock is used as a simple event variable.
  248. ready = thread.allocate_lock()
  249. ready.acquire()
  250. # Module globals are cleared before __del__ is run
  251. # So we save the functions in class dict
  252. class C:
  253. ensure = ctypes.pythonapi.PyGILState_Ensure
  254. release = ctypes.pythonapi.PyGILState_Release
  255. def __del__(self):
  256. state = self.ensure()
  257. self.release(state)
  258. def waitingThread():
  259. x = C()
  260. ready.release()
  261. time.sleep(100)
  262. thread.start_new_thread(waitingThread, ())
  263. ready.acquire() # Be sure the other thread is waiting.
  264. sys.exit(42)
  265. """])
  266. self.assertEqual(rc, 42)
  267. def test_finalize_with_trace(self):
  268. # Issue1733757
  269. # Avoid a deadlock when sys.settrace steps into threading._shutdown
  270. p = subprocess.Popen([sys.executable, "-c", """if 1:
  271. import sys, threading
  272. # A deadlock-killer, to prevent the
  273. # testsuite to hang forever
  274. def killer():
  275. import os, time
  276. time.sleep(2)
  277. print 'program blocked; aborting'
  278. os._exit(2)
  279. t = threading.Thread(target=killer)
  280. t.daemon = True
  281. t.start()
  282. # This is the trace function
  283. def func(frame, event, arg):
  284. threading.current_thread()
  285. return func
  286. sys.settrace(func)
  287. """],
  288. stdout=subprocess.PIPE,
  289. stderr=subprocess.PIPE)
  290. self.addCleanup(p.stdout.close)
  291. self.addCleanup(p.stderr.close)
  292. stdout, stderr = p.communicate()
  293. rc = p.returncode
  294. self.assertFalse(rc == 2, "interpreted was blocked")
  295. self.assertTrue(rc == 0,
  296. "Unexpected error: " + repr(stderr))
  297. def test_join_nondaemon_on_shutdown(self):
  298. # Issue 1722344
  299. # Raising SystemExit skipped threading._shutdown
  300. p = subprocess.Popen([sys.executable, "-c", """if 1:
  301. import threading
  302. from time import sleep
  303. def child():
  304. sleep(1)
  305. # As a non-daemon thread we SHOULD wake up and nothing
  306. # should be torn down yet
  307. print "Woke up, sleep function is:", sleep
  308. threading.Thread(target=child).start()
  309. raise SystemExit
  310. """],
  311. stdout=subprocess.PIPE,
  312. stderr=subprocess.PIPE)
  313. self.addCleanup(p.stdout.close)
  314. self.addCleanup(p.stderr.close)
  315. stdout, stderr = p.communicate()
  316. self.assertEqual(stdout.strip(),
  317. "Woke up, sleep function is: <built-in function sleep>")
  318. stderr = re.sub(r"^\[\d+ refs\]", "", stderr, re.MULTILINE).strip()
  319. self.assertEqual(stderr, "")
  320. def test_enumerate_after_join(self):
  321. # Try hard to trigger #1703448: a thread is still returned in
  322. # threading.enumerate() after it has been join()ed.
  323. enum = threading.enumerate
  324. old_interval = sys.getcheckinterval()
  325. try:
  326. for i in xrange(1, 100):
  327. # Try a couple times at each thread-switching interval
  328. # to get more interleavings.
  329. sys.setcheckinterval(i // 5)
  330. t = threading.Thread(target=lambda: None)
  331. t.start()
  332. t.join()
  333. l = enum()
  334. self.assertNotIn(t, l,
  335. "#1703448 triggered after %d trials: %s" % (i, l))
  336. finally:
  337. sys.setcheckinterval(old_interval)
  338. @test.test_support.cpython_only
  339. def test_no_refcycle_through_target(self):
  340. class RunSelfFunction(object):
  341. def __init__(self, should_raise):
  342. # The links in this refcycle from Thread back to self
  343. # should be cleaned up when the thread completes.
  344. self.should_raise = should_raise
  345. self.thread = threading.Thread(target=self._run,
  346. args=(self,),
  347. kwargs={'yet_another':self})
  348. self.thread.start()
  349. def _run(self, other_ref, yet_another):
  350. if self.should_raise:
  351. raise SystemExit
  352. cyclic_object = RunSelfFunction(should_raise=False)
  353. weak_cyclic_object = weakref.ref(cyclic_object)
  354. cyclic_object.thread.join()
  355. del cyclic_object
  356. self.assertEqual(None, weak_cyclic_object(),
  357. msg=('%d references still around' %
  358. sys.getrefcount(weak_cyclic_object())))
  359. raising_cyclic_object = RunSelfFunction(should_raise=True)
  360. weak_raising_cyclic_object = weakref.ref(raising_cyclic_object)
  361. raising_cyclic_object.thread.join()
  362. del raising_cyclic_object
  363. self.assertEqual(None, weak_raising_cyclic_object(),
  364. msg=('%d references still around' %
  365. sys.getrefcount(weak_raising_cyclic_object())))
  366. class ThreadJoinOnShutdown(BaseTestCase):
  367. def _run_and_join(self, script):
  368. script = """if 1:
  369. import sys, os, time, threading
  370. # a thread, which waits for the main program to terminate
  371. def joiningfunc(mainthread):
  372. mainthread.join()
  373. print 'end of thread'
  374. # stdout is fully buffered because not a tty, we have to flush
  375. # before exit.
  376. sys.stdout.flush()
  377. \n""" + script
  378. p = subprocess.Popen([sys.executable, "-c", script], stdout=subprocess.PIPE)
  379. rc = p.wait()
  380. data = p.stdout.read().replace('\r', '')
  381. p.stdout.close()
  382. self.assertEqual(data, "end of main\nend of thread\n")
  383. self.assertFalse(rc == 2, "interpreter was blocked")
  384. self.assertTrue(rc == 0, "Unexpected error")
  385. def test_1_join_on_shutdown(self):
  386. # The usual case: on exit, wait for a non-daemon thread
  387. script = """if 1:
  388. import os
  389. t = threading.Thread(target=joiningfunc,
  390. args=(threading.current_thread(),))
  391. t.start()
  392. time.sleep(0.1)
  393. print 'end of main'
  394. """
  395. self._run_and_join(script)
  396. def test_2_join_in_forked_process(self):
  397. # Like the test above, but from a forked interpreter
  398. import os
  399. if not hasattr(os, 'fork'):
  400. return
  401. script = """if 1:
  402. childpid = os.fork()
  403. if childpid != 0:
  404. os.waitpid(childpid, 0)
  405. sys.exit(0)
  406. t = threading.Thread(target=joiningfunc,
  407. args=(threading.current_thread(),))
  408. t.start()
  409. print 'end of main'
  410. """
  411. self._run_and_join(script)
  412. def test_3_join_in_forked_from_thread(self):
  413. # Like the test above, but fork() was called from a worker thread
  414. # In the forked process, the main Thread object must be marked as stopped.
  415. import os
  416. if not hasattr(os, 'fork'):
  417. return
  418. # Skip platforms with known problems forking from a worker thread.
  419. # See http://bugs.python.org/issue3863.
  420. if sys.platform in ('freebsd4', 'freebsd5', 'freebsd6', 'netbsd5',
  421. 'os2emx'):
  422. print >>sys.stderr, ('Skipping test_3_join_in_forked_from_thread'
  423. ' due to known OS bugs on'), sys.platform
  424. return
  425. script = """if 1:
  426. main_thread = threading.current_thread()
  427. def worker():
  428. childpid = os.fork()
  429. if childpid != 0:
  430. os.waitpid(childpid, 0)
  431. sys.exit(0)
  432. t = threading.Thread(target=joiningfunc,
  433. args=(main_thread,))
  434. print 'end of main'
  435. t.start()
  436. t.join() # Should not block: main_thread is already stopped
  437. w = threading.Thread(target=worker)
  438. w.start()
  439. """
  440. self._run_and_join(script)
  441. def assertScriptHasOutput(self, script, expected_output):
  442. p = subprocess.Popen([sys.executable, "-c", script],
  443. stdout=subprocess.PIPE)
  444. rc = p.wait()
  445. data = p.stdout.read().decode().replace('\r', '')
  446. self.assertEqual(rc, 0, "Unexpected error")
  447. self.assertEqual(data, expected_output)
  448. @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
  449. def test_4_joining_across_fork_in_worker_thread(self):
  450. # There used to be a possible deadlock when forking from a child
  451. # thread. See http://bugs.python.org/issue6643.
  452. # Skip platforms with known problems forking from a worker thread.
  453. # See http://bugs.python.org/issue3863.
  454. if sys.platform in ('freebsd4', 'freebsd5', 'freebsd6', 'os2emx'):
  455. raise unittest.SkipTest('due to known OS bugs on ' + sys.platform)
  456. # The script takes the following steps:
  457. # - The main thread in the parent process starts a new thread and then
  458. # tries to join it.
  459. # - The join operation acquires the Lock inside the thread's _block
  460. # Condition. (See threading.py:Thread.join().)
  461. # - We stub out the acquire method on the condition to force it to wait
  462. # until the child thread forks. (See LOCK ACQUIRED HERE)
  463. # - The child thread forks. (See LOCK HELD and WORKER THREAD FORKS
  464. # HERE)
  465. # - The main thread of the parent process enters Condition.wait(),
  466. # which releases the lock on the child thread.
  467. # - The child process returns. Without the necessary fix, when the
  468. # main thread of the child process (which used to be the child thread
  469. # in the parent process) attempts to exit, it will try to acquire the
  470. # lock in the Thread._block Condition object and hang, because the
  471. # lock was held across the fork.
  472. script = """if 1:
  473. import os, time, threading
  474. finish_join = False
  475. start_fork = False
  476. def worker():
  477. # Wait until this thread's lock is acquired before forking to
  478. # create the deadlock.
  479. global finish_join
  480. while not start_fork:
  481. time.sleep(0.01)
  482. # LOCK HELD: Main thread holds lock across this call.
  483. childpid = os.fork()
  484. finish_join = True
  485. if childpid != 0:
  486. # Parent process just waits for child.
  487. os.waitpid(childpid, 0)
  488. # Child process should just return.
  489. w = threading.Thread(target=worker)
  490. # Stub out the private condition variable's lock acquire method.
  491. # This acquires the lock and then waits until the child has forked
  492. # before returning, which will release the lock soon after. If
  493. # someone else tries to fix this test case by acquiring this lock
  494. # before forking instead of resetting it, the test case will
  495. # deadlock when it shouldn't.
  496. condition = w._block
  497. orig_acquire = condition.acquire
  498. call_count_lock = threading.Lock()
  499. call_count = 0
  500. def my_acquire():
  501. global call_count
  502. global start_fork
  503. orig_acquire() # LOCK ACQUIRED HERE
  504. start_fork = True
  505. if call_count == 0:
  506. while not finish_join:
  507. time.sleep(0.01) # WORKER THREAD FORKS HERE
  508. with call_count_lock:
  509. call_count += 1
  510. condition.acquire = my_acquire
  511. w.start()
  512. w.join()
  513. print('end of main')
  514. """
  515. self.assertScriptHasOutput(script, "end of main\n")
  516. @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
  517. def test_5_clear_waiter_locks_to_avoid_crash(self):
  518. # Check that a spawned thread that forks doesn't segfault on certain
  519. # platforms, namely OS X. This used to happen if there was a waiter
  520. # lock in the thread's condition variable's waiters list. Even though
  521. # we know the lock will be held across the fork, it is not safe to
  522. # release locks held across forks on all platforms, so releasing the
  523. # waiter lock caused a segfault on OS X. Furthermore, since locks on
  524. # OS X are (as of this writing) implemented with a mutex + condition
  525. # variable instead of a semaphore, while we know that the Python-level
  526. # lock will be acquired, we can't know if the internal mutex will be
  527. # acquired at the time of the fork.
  528. # Skip platforms with known problems forking from a worker thread.
  529. # See http://bugs.python.org/issue3863.
  530. if sys.platform in ('freebsd4', 'freebsd5', 'freebsd6', 'os2emx'):
  531. raise unittest.SkipTest('due to known OS bugs on ' + sys.platform)
  532. script = """if True:
  533. import os, time, threading
  534. start_fork = False
  535. def worker():
  536. # Wait until the main thread has attempted to join this thread
  537. # before continuing.
  538. while not start_fork:
  539. time.sleep(0.01)
  540. childpid = os.fork()
  541. if childpid != 0:
  542. # Parent process just waits for child.
  543. (cpid, rc) = os.waitpid(childpid, 0)
  544. assert cpid == childpid
  545. assert rc == 0
  546. print('end of worker thread')
  547. else:
  548. # Child process should just return.
  549. pass
  550. w = threading.Thread(target=worker)
  551. # Stub out the private condition variable's _release_save method.
  552. # This releases the condition's lock and flips the global that
  553. # causes the worker to fork. At this point, the problematic waiter
  554. # lock has been acquired once by the waiter and has been put onto
  555. # the waiters list.
  556. condition = w._block
  557. orig_release_save = condition._release_save
  558. def my_release_save():
  559. global start_fork
  560. orig_release_save()
  561. # Waiter lock held here, condition lock released.
  562. start_fork = True
  563. condition._release_save = my_release_save
  564. w.start()
  565. w.join()
  566. print('end of main thread')
  567. """
  568. output = "end of worker thread\nend of main thread\n"
  569. self.assertScriptHasOutput(script, output)
  570. class ThreadingExceptionTests(BaseTestCase):
  571. # A RuntimeError should be raised if Thread.start() is called
  572. # multiple times.
  573. def test_start_thread_again(self):
  574. thread = threading.Thread()
  575. thread.start()
  576. self.assertRaises(RuntimeError, thread.start)
  577. def test_joining_current_thread(self):
  578. current_thread = threading.current_thread()
  579. self.assertRaises(RuntimeError, current_thread.join);
  580. def test_joining_inactive_thread(self):
  581. thread = threading.Thread()
  582. self.assertRaises(RuntimeError, thread.join)
  583. def test_daemonize_active_thread(self):
  584. thread = threading.Thread()
  585. thread.start()
  586. self.assertRaises(RuntimeError, setattr, thread, "daemon", True)
  587. class LockTests(lock_tests.LockTests):
  588. locktype = staticmethod(threading.Lock)
  589. class RLockTests(lock_tests.RLockTests):
  590. locktype = staticmethod(threading.RLock)
  591. class EventTests(lock_tests.EventTests):
  592. eventtype = staticmethod(threading.Event)
  593. class ConditionAsRLockTests(lock_tests.RLockTests):
  594. # An Condition uses an RLock by default and exports its API.
  595. locktype = staticmethod(threading.Condition)
  596. class ConditionTests(lock_tests.ConditionTests):
  597. condtype = staticmethod(threading.Condition)
  598. class SemaphoreTests(lock_tests.SemaphoreTests):
  599. semtype = staticmethod(threading.Semaphore)
  600. class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
  601. semtype = staticmethod(threading.BoundedSemaphore)
  602. @unittest.skipUnless(sys.platform == 'darwin', 'test macosx problem')
  603. def test_recursion_limit(self):
  604. # Issue 9670
  605. # test that excessive recursion within a non-main thread causes
  606. # an exception rather than crashing the interpreter on platforms
  607. # like Mac OS X or FreeBSD which have small default stack sizes
  608. # for threads
  609. script = """if True:
  610. import threading
  611. def recurse():
  612. return recurse()
  613. def outer():
  614. try:
  615. recurse()
  616. except RuntimeError:
  617. pass
  618. w = threading.Thread(target=outer)
  619. w.start()
  620. w.join()
  621. print('end of main thread')
  622. """
  623. expected_output = "end of main thread\n"
  624. p = subprocess.Popen([sys.executable, "-c", script],
  625. stdout=subprocess.PIPE)
  626. stdout, stderr = p.communicate()
  627. data = stdout.decode().replace('\r', '')
  628. self.assertEqual(p.returncode, 0, "Unexpected error")
  629. self.assertEqual(data, expected_output)
  630. def test_main():
  631. test.test_support.run_unittest(LockTests, RLockTests, EventTests,
  632. ConditionAsRLockTests, ConditionTests,
  633. SemaphoreTests, BoundedSemaphoreTests,
  634. ThreadTests,
  635. ThreadJoinOnShutdown,
  636. ThreadingExceptionTests,
  637. )
  638. if __name__ == "__main__":
  639. test_main()