bpo-40094: Enhance threading tests (GH-19260)

* Rewrite test_thread.test_forkinthread() to use
  support.wait_process() and wait for the child process in the main
  thread, not in the spawned thread.
* test_threading now uses support.wait_process() and checks the child
  process exit code to detect crashes.
This commit is contained in:
Victor Stinner 2020-03-31 21:49:44 +02:00 committed by GitHub
parent 27c6231f58
commit a9f9687a7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 31 deletions

View file

@ -225,30 +225,31 @@ class TestForkInThread(unittest.TestCase):
@unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork') @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork')
@support.reap_threads @support.reap_threads
def test_forkinthread(self): def test_forkinthread(self):
status = "not set" pid = None
def thread1(): def fork_thread(read_fd, write_fd):
nonlocal status nonlocal pid
# fork in a thread # fork in a thread
pid = os.fork() pid = os.fork()
if pid == 0: if pid:
# child # parent process
try: return
os.close(self.read_fd)
os.write(self.write_fd, b"OK") # child process
finally: try:
os._exit(0) os.close(read_fd)
else: os.write(write_fd, b"OK")
# parent finally:
os.close(self.write_fd) os._exit(0)
pid, status = os.waitpid(pid, 0)
with support.wait_threads_exit(): with support.wait_threads_exit():
thread.start_new_thread(thread1, ()) thread.start_new_thread(fork_thread, (self.read_fd, self.write_fd))
self.assertEqual(os.read(self.read_fd, 2), b"OK", self.assertEqual(os.read(self.read_fd, 2), b"OK")
"Unable to fork() in thread") os.close(self.write_fd)
self.assertEqual(status, 0)
self.assertIsNotNone(pid)
support.wait_process(pid, exitcode=0)
def tearDown(self): def tearDown(self):
try: try:

View file

@ -485,9 +485,7 @@ class ThreadTests(BaseTestCase):
else: else:
t.join() t.join()
pid, status = os.waitpid(pid, 0) support.wait_process(pid, exitcode=10)
self.assertTrue(os.WIFEXITED(status))
self.assertEqual(10, os.WEXITSTATUS(status))
def test_main_thread(self): def test_main_thread(self):
main = threading.main_thread() main = threading.main_thread()
@ -507,6 +505,7 @@ class ThreadTests(BaseTestCase):
def test_main_thread_after_fork(self): def test_main_thread_after_fork(self):
code = """if 1: code = """if 1:
import os, threading import os, threading
from test import support
pid = os.fork() pid = os.fork()
if pid == 0: if pid == 0:
@ -515,7 +514,7 @@ class ThreadTests(BaseTestCase):
print(main.ident == threading.current_thread().ident) print(main.ident == threading.current_thread().ident)
print(main.ident == threading.get_ident()) print(main.ident == threading.get_ident())
else: else:
os.waitpid(pid, 0) support.wait_process(pid, exitcode=0)
""" """
_, out, err = assert_python_ok("-c", code) _, out, err = assert_python_ok("-c", code)
data = out.decode().replace('\r', '') data = out.decode().replace('\r', '')
@ -528,6 +527,7 @@ class ThreadTests(BaseTestCase):
def test_main_thread_after_fork_from_nonmain_thread(self): def test_main_thread_after_fork_from_nonmain_thread(self):
code = """if 1: code = """if 1:
import os, threading, sys import os, threading, sys
from test import support
def f(): def f():
pid = os.fork() pid = os.fork()
@ -540,7 +540,7 @@ class ThreadTests(BaseTestCase):
# we have to flush before exit. # we have to flush before exit.
sys.stdout.flush() sys.stdout.flush()
else: else:
os.waitpid(pid, 0) support.wait_process(pid, exitcode=0)
th = threading.Thread(target=f) th = threading.Thread(target=f)
th.start() th.start()
@ -813,11 +813,15 @@ class ThreadJoinOnShutdown(BaseTestCase):
def test_2_join_in_forked_process(self): def test_2_join_in_forked_process(self):
# Like the test above, but from a forked interpreter # Like the test above, but from a forked interpreter
script = """if 1: script = """if 1:
from test import support
childpid = os.fork() childpid = os.fork()
if childpid != 0: if childpid != 0:
os.waitpid(childpid, 0) # parent process
support.wait_process(childpid, exitcode=0)
sys.exit(0) sys.exit(0)
# child process
t = threading.Thread(target=joiningfunc, t = threading.Thread(target=joiningfunc,
args=(threading.current_thread(),)) args=(threading.current_thread(),))
t.start() t.start()
@ -832,13 +836,17 @@ class ThreadJoinOnShutdown(BaseTestCase):
# In the forked process, the main Thread object must be marked as stopped. # In the forked process, the main Thread object must be marked as stopped.
script = """if 1: script = """if 1:
from test import support
main_thread = threading.current_thread() main_thread = threading.current_thread()
def worker(): def worker():
childpid = os.fork() childpid = os.fork()
if childpid != 0: if childpid != 0:
os.waitpid(childpid, 0) # parent process
support.wait_process(childpid, exitcode=0)
sys.exit(0) sys.exit(0)
# child process
t = threading.Thread(target=joiningfunc, t = threading.Thread(target=joiningfunc,
args=(main_thread,)) args=(main_thread,))
print('end of main') print('end of main')
@ -901,9 +909,9 @@ class ThreadJoinOnShutdown(BaseTestCase):
# just fork a child process and wait it # just fork a child process and wait it
pid = os.fork() pid = os.fork()
if pid > 0: if pid > 0:
os.waitpid(pid, 0) support.wait_process(pid, exitcode=50)
else: else:
os._exit(0) os._exit(50)
# start a bunch of threads that will fork() child processes # start a bunch of threads that will fork() child processes
threads = [] threads = []
@ -930,12 +938,11 @@ class ThreadJoinOnShutdown(BaseTestCase):
if pid == 0: if pid == 0:
# check that threads states have been cleared # check that threads states have been cleared
if len(sys._current_frames()) == 1: if len(sys._current_frames()) == 1:
os._exit(0) os._exit(51)
else: else:
os._exit(1) os._exit(52)
else: else:
_, status = os.waitpid(pid, 0) support.wait_process(pid, exitcode=51)
self.assertEqual(0, status)
for t in threads: for t in threads:
t.join() t.join()