test_thread.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import os
  2. import unittest
  3. import random
  4. from test import support
  5. from test.support import threading_helper
  6. import _thread as thread
  7. import time
  8. import weakref
  9. from test import lock_tests
  10. threading_helper.requires_working_threading(module=True)
  11. NUMTASKS = 10
  12. NUMTRIPS = 3
  13. POLL_SLEEP = 0.010 # seconds = 10 ms
  14. _print_mutex = thread.allocate_lock()
  15. def verbose_print(arg):
  16. """Helper function for printing out debugging output."""
  17. if support.verbose:
  18. with _print_mutex:
  19. print(arg)
  20. class BasicThreadTest(unittest.TestCase):
  21. def setUp(self):
  22. self.done_mutex = thread.allocate_lock()
  23. self.done_mutex.acquire()
  24. self.running_mutex = thread.allocate_lock()
  25. self.random_mutex = thread.allocate_lock()
  26. self.created = 0
  27. self.running = 0
  28. self.next_ident = 0
  29. key = threading_helper.threading_setup()
  30. self.addCleanup(threading_helper.threading_cleanup, *key)
  31. class ThreadRunningTests(BasicThreadTest):
  32. def newtask(self):
  33. with self.running_mutex:
  34. self.next_ident += 1
  35. verbose_print("creating task %s" % self.next_ident)
  36. thread.start_new_thread(self.task, (self.next_ident,))
  37. self.created += 1
  38. self.running += 1
  39. def task(self, ident):
  40. with self.random_mutex:
  41. delay = random.random() / 10000.0
  42. verbose_print("task %s will run for %sus" % (ident, round(delay*1e6)))
  43. time.sleep(delay)
  44. verbose_print("task %s done" % ident)
  45. with self.running_mutex:
  46. self.running -= 1
  47. if self.created == NUMTASKS and self.running == 0:
  48. self.done_mutex.release()
  49. def test_starting_threads(self):
  50. with threading_helper.wait_threads_exit():
  51. # Basic test for thread creation.
  52. for i in range(NUMTASKS):
  53. self.newtask()
  54. verbose_print("waiting for tasks to complete...")
  55. self.done_mutex.acquire()
  56. verbose_print("all tasks done")
  57. def test_stack_size(self):
  58. # Various stack size tests.
  59. self.assertEqual(thread.stack_size(), 0, "initial stack size is not 0")
  60. thread.stack_size(0)
  61. self.assertEqual(thread.stack_size(), 0, "stack_size not reset to default")
  62. @unittest.skipIf(os.name not in ("nt", "posix"), 'test meant for nt and posix')
  63. def test_nt_and_posix_stack_size(self):
  64. try:
  65. thread.stack_size(4096)
  66. except ValueError:
  67. verbose_print("caught expected ValueError setting "
  68. "stack_size(4096)")
  69. except thread.error:
  70. self.skipTest("platform does not support changing thread stack "
  71. "size")
  72. fail_msg = "stack_size(%d) failed - should succeed"
  73. for tss in (262144, 0x100000, 0):
  74. thread.stack_size(tss)
  75. self.assertEqual(thread.stack_size(), tss, fail_msg % tss)
  76. verbose_print("successfully set stack_size(%d)" % tss)
  77. for tss in (262144, 0x100000):
  78. verbose_print("trying stack_size = (%d)" % tss)
  79. self.next_ident = 0
  80. self.created = 0
  81. with threading_helper.wait_threads_exit():
  82. for i in range(NUMTASKS):
  83. self.newtask()
  84. verbose_print("waiting for all tasks to complete")
  85. self.done_mutex.acquire()
  86. verbose_print("all tasks done")
  87. thread.stack_size(0)
  88. def test__count(self):
  89. # Test the _count() function.
  90. orig = thread._count()
  91. mut = thread.allocate_lock()
  92. mut.acquire()
  93. started = []
  94. def task():
  95. started.append(None)
  96. mut.acquire()
  97. mut.release()
  98. with threading_helper.wait_threads_exit():
  99. thread.start_new_thread(task, ())
  100. while not started:
  101. time.sleep(POLL_SLEEP)
  102. self.assertEqual(thread._count(), orig + 1)
  103. # Allow the task to finish.
  104. mut.release()
  105. # The only reliable way to be sure that the thread ended from the
  106. # interpreter's point of view is to wait for the function object to be
  107. # destroyed.
  108. done = []
  109. wr = weakref.ref(task, lambda _: done.append(None))
  110. del task
  111. while not done:
  112. time.sleep(POLL_SLEEP)
  113. support.gc_collect() # For PyPy or other GCs.
  114. self.assertEqual(thread._count(), orig)
  115. def test_unraisable_exception(self):
  116. def task():
  117. started.release()
  118. raise ValueError("task failed")
  119. started = thread.allocate_lock()
  120. with support.catch_unraisable_exception() as cm:
  121. with threading_helper.wait_threads_exit():
  122. started.acquire()
  123. thread.start_new_thread(task, ())
  124. started.acquire()
  125. self.assertEqual(str(cm.unraisable.exc_value), "task failed")
  126. self.assertIs(cm.unraisable.object, task)
  127. self.assertEqual(cm.unraisable.err_msg,
  128. "Exception ignored in thread started by")
  129. self.assertIsNotNone(cm.unraisable.exc_traceback)
  130. class Barrier:
  131. def __init__(self, num_threads):
  132. self.num_threads = num_threads
  133. self.waiting = 0
  134. self.checkin_mutex = thread.allocate_lock()
  135. self.checkout_mutex = thread.allocate_lock()
  136. self.checkout_mutex.acquire()
  137. def enter(self):
  138. self.checkin_mutex.acquire()
  139. self.waiting = self.waiting + 1
  140. if self.waiting == self.num_threads:
  141. self.waiting = self.num_threads - 1
  142. self.checkout_mutex.release()
  143. return
  144. self.checkin_mutex.release()
  145. self.checkout_mutex.acquire()
  146. self.waiting = self.waiting - 1
  147. if self.waiting == 0:
  148. self.checkin_mutex.release()
  149. return
  150. self.checkout_mutex.release()
  151. class BarrierTest(BasicThreadTest):
  152. def test_barrier(self):
  153. with threading_helper.wait_threads_exit():
  154. self.bar = Barrier(NUMTASKS)
  155. self.running = NUMTASKS
  156. for i in range(NUMTASKS):
  157. thread.start_new_thread(self.task2, (i,))
  158. verbose_print("waiting for tasks to end")
  159. self.done_mutex.acquire()
  160. verbose_print("tasks done")
  161. def task2(self, ident):
  162. for i in range(NUMTRIPS):
  163. if ident == 0:
  164. # give it a good chance to enter the next
  165. # barrier before the others are all out
  166. # of the current one
  167. delay = 0
  168. else:
  169. with self.random_mutex:
  170. delay = random.random() / 10000.0
  171. verbose_print("task %s will run for %sus" %
  172. (ident, round(delay * 1e6)))
  173. time.sleep(delay)
  174. verbose_print("task %s entering %s" % (ident, i))
  175. self.bar.enter()
  176. verbose_print("task %s leaving barrier" % ident)
  177. with self.running_mutex:
  178. self.running -= 1
  179. # Must release mutex before releasing done, else the main thread can
  180. # exit and set mutex to None as part of global teardown; then
  181. # mutex.release() raises AttributeError.
  182. finished = self.running == 0
  183. if finished:
  184. self.done_mutex.release()
  185. class LockTests(lock_tests.LockTests):
  186. locktype = thread.allocate_lock
  187. class TestForkInThread(unittest.TestCase):
  188. def setUp(self):
  189. self.read_fd, self.write_fd = os.pipe()
  190. @support.requires_fork()
  191. @threading_helper.reap_threads
  192. def test_forkinthread(self):
  193. pid = None
  194. def fork_thread(read_fd, write_fd):
  195. nonlocal pid
  196. # fork in a thread
  197. pid = os.fork()
  198. if pid:
  199. # parent process
  200. return
  201. # child process
  202. try:
  203. os.close(read_fd)
  204. os.write(write_fd, b"OK")
  205. finally:
  206. os._exit(0)
  207. with threading_helper.wait_threads_exit():
  208. thread.start_new_thread(fork_thread, (self.read_fd, self.write_fd))
  209. self.assertEqual(os.read(self.read_fd, 2), b"OK")
  210. os.close(self.write_fd)
  211. self.assertIsNotNone(pid)
  212. support.wait_process(pid, exitcode=0)
  213. def tearDown(self):
  214. try:
  215. os.close(self.read_fd)
  216. except OSError:
  217. pass
  218. try:
  219. os.close(self.write_fd)
  220. except OSError:
  221. pass
  222. if __name__ == "__main__":
  223. unittest.main()