Free-threading: `signal.pthread_kill` deadlocks child threads
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 77.2k
- Fork
- 35.9k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
Context
The Dask threaded scheduler (e.g. dask.array or dask.dataframe without dask.distributed) blocks the main thread when the user calls compute() and internally spawns a concurrent.futures.ThreadPoolExecutor to schedule tasks. If a user hits Ctrl+C, they expect the program to terminate soon (at most waiting for the tasks that are halfway through execution to terminate).
Bug report
This sequence frequently deadlocks on Linux x64 free-threaded:
- main thread calls
threading.Thread.start - another thread calls
signal.pthread_kill(main_thread, signal.SIGINT), which lands afterstart()has returned but before the thread'stargetfunction has started executing - main thread handles the resulting
KeyboardInterrupt - main thread calls
.join()on the child thread started at point 1. - frequently,
.join()deadlocks. A second SIGINT (e.g. from Ctrl+C) unblocks it.
Reproducer
import signal
import threading
USE_BARRIER = False
NTHREADS = 20
def demo():
main_thread = threading.get_ident()
barrier = threading.Barrier(NTHREADS) if USE_BARRIER else None
thread_can_die = threading.Event()
def f() -> None:
if barrier:
barrier.wait()
thread_can_die.wait()
def interrupt() -> None:
if barrier:
barrier.wait()
signal.pthread_kill(main_thread, signal.SIGINT)
threads = []
for _ in range(NTHREADS - 1):
t = threading.Thread(target=f)
t.start()
threads.append(t)
try:
t = threading.Thread(target=interrupt)
t.start()
threads.append(t)
for t in threads:
t.join()
except KeyboardInterrupt:
pass
else:
assert False, "Expected KeyboardInterrupt"
thread_can_die.set()
for t in threads:
t.join()
if __name__ == "__main__":
i = 0
while True:
print(i, flush=True)
demo()
i += 1
On these setups, the above program hangs within a handful of cycles:
- Linux x64 3.14.5t
- Linux x64 git tip #7a468a101268d2b13105f94ae027df8b502d0c87 (built with
Tools/pixi-packages/freethreading)
Cannot reproduce on
- Linux x64 3.14.5
Setting USE_BARRIER=True, which ensures that all child threads are already fully started when the SIGINT lands on the main thread, makes the issue disappear.
CPython versions tested on:
CPython main branch, 3.14
Operating systems tested on:
Linux
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu bằng cách chạy reproducer được cung cấp trên Linux x64 free-threaded CPython, sau đó theo dõi tương tác giữa threading.Thread.start(), threading.Thread.join() và signal.pthread_kill(). Công việc được hoàn thành khi reproducer không còn bị deadlock nếu SIGINT đến trước khi target của một luồng con bắt đầu, đồng thời hành vi KeyboardInterrupt như mong đợi vẫn được giữ nguyên.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- operating-systems
- Loại issue
- Lỗi
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 45/100