fix: 修复流式输入 ctx 取消时 handler 线程永久阻塞泄漏的问题

流式输入模式下,handler 线程阻塞在 _ChunkIter.__next__ → chunk_q.get()
等待下一个输入块时,如果 ctx 取消导致 Go 关闭连接,_ConnMux._reader
原来只往 call_q 推了 None、给 _active_tids 里的线程注入 InterruptedError,
没有处理 chunk_q。而 PyThreadState_SetAsyncExc 打断不了阻塞在
queue.Queue.get()(无 timeout)上的线程,因为它是纯 C 层等待、不会回到
字节码解释循环检查待处理异常,导致这个线程永久卡死、泄漏。

修复:连接关闭时也往 chunk_q 推一个 None 哨兵,_ChunkIter.__next__ 已经
把 None 当作流结束处理,会正常抛 StopIteration 让线程退出。

新增 pool_test.go 里的 TestStreamInputCancelDoesNotLeakThread 复现并验证
修复(通过对比 count_threads() 前后线程数)。python 包版本号同步升到 0.1.5。
This commit is contained in:
2026-07-23 14:07:47 +08:00
parent eb6ebd8632
commit 4f74627be1
4 changed files with 54 additions and 1 deletions
+5
View File
@@ -247,6 +247,11 @@ class _ConnMux:
if msg is None:
# 连接关闭:唤醒主循环,中断所有正在执行的函数,并唤醒所有 call_go 等待
self.call_q.put(None)
# 唤醒卡在 chunk_q.get() 上的流式输入线程(_ChunkIter)——
# PyThreadState_SetAsyncExc 打断不了阻塞在 queue.Queue.get() 上的
# C 层等待,必须显式推一个哨兵值,_ChunkIter.__next__ 已经把
# None 当作流结束处理,会正常抛 StopIteration 退出。
self.chunk_q.put(None)
with self._lock:
for tid in self._active_tids.values():
_raise_in_thread(tid, InterruptedError)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "gobridge"
version = "0.1.4"
version = "0.1.5"
description = "Python 端库,配合 Go 侧 gobridge 使用"
requires-python = ">=3.10"