diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 3060af44fd7e3d..59e1621bbacb58 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -724,6 +724,55 @@ def func(): self.assertEqual(err.decode('utf-8'), "") self.assertEqual(data, "Thread-1 (func)\nTrue\nTrue\n") + @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") + @support.requires_fork() + @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") + def test_main_thread_after_fork_from_foreign_thread(self): + code = """if 1: + import os, threading, sys, traceback, _thread + from test import support + + def func(lock): + # call current_thread() before fork to allocate DummyThread + current = threading.current_thread() + print(current.name) + # flush before fork, so child won't flush it again + sys.stdout.flush() + pid = os.fork() + if pid == 0: + main = threading.main_thread() + print(main.ident == threading.current_thread().ident) + print(main.ident == threading.get_ident()) + # stdout is fully buffered because not a tty, + # we have to flush before exit. + sys.stdout.flush() + try: + threading._shutdown() + os._exit(0) + except: + os._exit(1) + else: + try: + support.wait_process(pid, exitcode=0) + except Exception as e: + # avoid 'could not acquire lock for + # <_io.BufferedWriter name=''> at interpreter shutdown,' + traceback.print_exc() + sys.stderr.flush() + finally: + lock.release() + + join_lock = _thread.allocate_lock() + join_lock.acquire() + th = _thread.start_new_thread(func, (join_lock,)) + join_lock.acquire() + """ + # "DeprecationWarning: This process is multi-threaded, use of fork() may lead to deadlocks in the child" + _, out, err = assert_python_ok("-W", "ignore::DeprecationWarning", "-c", code) + data = out.decode().replace('\r', '') + self.assertEqual(err, b"") + self.assertEqual(data, "Dummy-1\nTrue\nTrue\n") + def test_main_thread_during_shutdown(self): # bpo-31516: current_thread() should still point to the main thread # at shutdown diff --git a/Lib/threading.py b/Lib/threading.py index 85aff58968082d..3f809fa4d6c597 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1489,7 +1489,7 @@ class _DummyThread(Thread): def __init__(self): Thread.__init__(self, name=_newname("Dummy-%d"), daemon=_daemon_threads_allowed()) - + self._set_tstate_lock() self._started.set() self._set_ident() if _HAVE_THREAD_NATIVE_ID: diff --git a/Misc/NEWS.d/next/Library/2023-03-08-00-02-30.gh-issue-102512.LiugDr.rst b/Misc/NEWS.d/next/Library/2023-03-08-00-02-30.gh-issue-102512.LiugDr.rst new file mode 100644 index 00000000000000..81f95a06ffb941 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-08-00-02-30.gh-issue-102512.LiugDr.rst @@ -0,0 +1,2 @@ +Fix handling threading after os.fork() called from a foreign thread (aka +DummyThread).