Description
Bug report
threading._shutdown()
relies on _main_thread
having _tstate_lock
not None
(there is assert for that). When fork is called from a DummyThread (in my case, that's a thread created by (Py)Qt), it gets promoted to main thread, but remains very simplistic DummyThread. Especially, nobody initializes its _tstate_lock
. threading._after_fork()
handles the case of current thread not being in _active
dict at all (by creating new MainThread object), but it doesn't handle the case of having DummyThread there already. This results in AssertionError in thread shutdown method - which for example confuses multiprocessing.Process
(it gets exit code 1, even if the process function was successful).
Reproducer:
#!/usr/bin/python3
import threading
import multiprocessing
import _thread
class Bar(multiprocessing.Process):
def run(self):
print("process")
def run_thread(lock):
# the call to current_thread() is crucial for reproducer - it allocates
# DummyThread()
print(f"thread: {threading.current_thread()}")
p = Bar()
p.start()
p.join()
print(f"proc exit code: {p.exitcode}")
lock.release()
def main():
lock = _thread.allocate_lock()
lock.acquire()
t = _thread.start_new_thread(run_thread, (lock,))
# t.join
lock.acquire()
print(f"thread exit")
main()
It should print:
thread: <_DummyThread(Dummy-1, started daemon 135243893053120)>
process
proc exit code: 0
thread exit
but it prints:
thread: <_DummyThread(Dummy-1, started daemon 135243893053120)>
process
proc exit code: 1
thread exit
(see exit code difference)
multiprocessing.Process
(or rather multiprocessing.popen_fork.Popen._launch()
to be specific) swallows the exception, but adding some debug prints there I get:
Traceback (most recent call last):
File "/usr/lib64/python3.11/multiprocessing/popen_fork.py", line 71, in _launch
code = process_obj._bootstrap(parent_sentinel=child_r)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/multiprocessing/process.py", line 332, in _bootstrap
threading._shutdown()
File "/usr/lib64/python3.11/threading.py", line 1553, in _shutdown
assert tlock is not None
^^^^^^^^^^^^^^^^^
Your environment
- CPython versions tested on: 3.11.2
- Operating system and architecture: Fedora 37, x86_64
Linked PRs
- gh-102512: Fix threading after os.fork() called from a foreign thread #102517
- gh-102512: Fix threading after os.fork() called from a foreign thread #113261
- [3.12] gh-102512: Turn _DummyThread into _MainThread after os.fork() called from a foreign thread (GH-113261) #114430
- [3.11] gh-102512: Turn _DummyThread into _MainThread after os.fork() called from a foreign thread (GH-113261) #114431
Metadata
Metadata
Assignees
Labels
Projects
Status