-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
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
Activity
marmarek commentedon Mar 7, 2023
Simplified reproducer (not importing multiprocessing, but doing what it does directly):
It fails this way:
Add regression test for pythongh-102512
Initialize tstate_lock in DummyThread too
Add regression test for pythongh-102512
Initialize tstate_lock in DummyThread too
Yet another disgusting hack for python 3.11.2 bug
Yet another disgusting hack for python 3.11.2 bug
Yet another disgusting hack for python 3.11.2 bug
Add workaround for Python threading bug (DM-38669)
8 remaining items
gh-102512: Turn _DummyThread into _MainThread after os.fork() called …
pythongh-102512: Turn _DummyThread into _MainThread after os.fork() c…
[3.11] pythongh-102512: Turn _DummyThread into _MainThread after os.f…
[3.11] pythongh-102512: Turn _DummyThread into _MainThread after os.f…
serhiy-storchaka commentedon Jan 22, 2024
Thank you for your report and PR @marmarek. I created a more radical PR based on it. It is not only create a lock for a main thread after fork, it makes it non-demonic and changes it type.
Perhaps a similar should be done for normal threads, but this is a different issue. It can be an instance of a custom Thread subclass, so changing its type may be not safe.
[3.11] gh-102512: Turn _DummyThread into _MainThread after os.fork() …
pythongh-102512: Turn _DummyThread into _MainThread after os.fork() c…
[3.12] gh-102512: Turn _DummyThread into _MainThread after os.fork() …
pythongh-102512: Turn _DummyThread into _MainThread after os.fork() c…
pythongh-102512: Turn _DummyThread into _MainThread after os.fork() c…