summaryrefslogtreecommitdiff
path: root/Lib/test/test_threading.py
diff options
context:
space:
mode:
authorGregory P. Smith <greg@mad-scientist.com>2008-08-17 23:01:11 +0000
committerGregory P. Smith <greg@mad-scientist.com>2008-08-17 23:01:11 +0000
commit5e8dc97a098766a566b07df241c524fbd56d34ac (patch)
tree2d9d31372c73d994c7cc2938177b70a272cf8580 /Lib/test/test_threading.py
parent1d63a45d0d25acec0174bb291e6cd4c7c0d7c00d (diff)
downloadcpython-git-5e8dc97a098766a566b07df241c524fbd56d34ac.tar.gz
Backport of r65032 from trunk
Fixes Issue #874900: after an os.fork() call the threading module state is cleaned up in the child process to prevent deadlock and report proper thread counts if the new process uses the threading module.
Diffstat (limited to 'Lib/test/test_threading.py')
-rw-r--r--Lib/test/test_threading.py79
1 files changed, 78 insertions, 1 deletions
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 7c648186f2..e9899c92d9 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -220,8 +220,85 @@ class ThreadTests(unittest.TestCase):
sys.setcheckinterval(old_interval)
+class ThreadJoinOnShutdown(unittest.TestCase):
+
+ def _run_and_join(self, script):
+ script = """if 1:
+ import sys, os, time, threading
+
+ # a thread, which waits for the main program to terminate
+ def joiningfunc(mainthread):
+ mainthread.join()
+ print 'end of thread'
+ \n""" + script
+
+ import subprocess
+ p = subprocess.Popen([sys.executable, "-c", script], stdout=subprocess.PIPE)
+ rc = p.wait()
+ self.assertEqual(p.stdout.read(), "end of main\nend of thread\n")
+ self.failIf(rc == 2, "interpreter was blocked")
+ self.failUnless(rc == 0, "Unexpected error")
+
+ def test_1_join_on_shutdown(self):
+ # The usual case: on exit, wait for a non-daemon thread
+ script = """if 1:
+ import os
+ t = threading.Thread(target=joiningfunc,
+ args=(threading.currentThread(),))
+ t.start()
+ time.sleep(0.1)
+ print 'end of main'
+ """
+ self._run_and_join(script)
+
+
+ def test_2_join_in_forked_process(self):
+ # Like the test above, but from a forked interpreter
+ import os
+ if not hasattr(os, 'fork'):
+ return
+ script = """if 1:
+ childpid = os.fork()
+ if childpid != 0:
+ os.waitpid(childpid, 0)
+ sys.exit(0)
+
+ t = threading.Thread(target=joiningfunc,
+ args=(threading.currentThread(),))
+ t.start()
+ print 'end of main'
+ """
+ self._run_and_join(script)
+
+ def test_3_join_in_forked_from_thread(self):
+ # Like the test above, but fork() was called from a worker thread
+ # In the forked process, the main Thread object must be marked as stopped.
+ import os
+ if not hasattr(os, 'fork'):
+ return
+ script = """if 1:
+ main_thread = threading.currentThread()
+ def worker():
+ childpid = os.fork()
+ if childpid != 0:
+ os.waitpid(childpid, 0)
+ sys.exit(0)
+
+ t = threading.Thread(target=joiningfunc,
+ args=(main_thread,))
+ print 'end of main'
+ t.start()
+ t.join() # Should not block: main_thread is already stopped
+
+ w = threading.Thread(target=worker)
+ w.start()
+ """
+ self._run_and_join(script)
+
+
def test_main():
- test.test_support.run_unittest(ThreadTests)
+ test.test_support.run_unittest(ThreadTests,
+ ThreadJoinOnShutdown)
if __name__ == "__main__":
test_main()