refactor: enforce background task ownership

This commit is contained in:
jxxghp
2026-08-23 20:17:28 +08:00
parent 9f3be0ea4b
commit 59f020f226
8 changed files with 564 additions and 7 deletions
+95
View File
@@ -1,6 +1,7 @@
"""进程内后台任务登记与关停语义测试。"""
import asyncio
import threading
import pytest
@@ -84,3 +85,97 @@ def test_task_registry_runs_sync_function_and_tracks_until_completion() -> None:
release.set()
asyncio.run(scenario())
def test_task_registry_keeps_timed_out_sync_owner_until_real_completion() -> None:
"""同步线程超过关停预算后仍应保留 owner,不能把包装任务取消成伪完成。"""
async def scenario() -> None:
registry = TaskRegistry()
started = threading.Event()
release = threading.Event()
reports: list[dict[str, object]] = []
loop = asyncio.get_running_loop()
previous_handler = loop.get_exception_handler()
loop.set_exception_handler(lambda _, context: reports.append(context))
def worker() -> None:
"""模拟无法由 asyncio 取消、需要外部资源自行结束的同步工作。"""
started.set()
release.wait()
try:
task = registry.create_sync(worker, owner="test.sync-timeout")
assert await asyncio.to_thread(started.wait, 1.0)
await registry.shutdown(timeout_seconds=0.001)
assert not task.done()
assert [record.owner for record in registry.records] == [
"test.sync-timeout"
]
assert reports[-1]["owners"] == ("test.sync-timeout",)
release.set()
await task
await asyncio.sleep(0)
assert registry.records == ()
finally:
release.set()
loop.set_exception_handler(previous_handler)
asyncio.run(scenario())
def test_task_registry_keeps_stubborn_cancelled_task_visible() -> None:
"""协程清理超过预算时只收一次取消,并在最终退出后自动清理。"""
async def scenario() -> None:
registry = TaskRegistry()
started = asyncio.Event()
cleanup_started = asyncio.Event()
release = asyncio.Event()
cancellation_count = 0
reports: list[dict[str, object]] = []
loop = asyncio.get_running_loop()
previous_handler = loop.get_exception_handler()
loop.set_exception_handler(lambda _, context: reports.append(context))
async def worker() -> None:
"""模拟收到取消后仍必须完成的异步清理。"""
nonlocal cancellation_count
started.set()
try:
await asyncio.Event().wait()
except asyncio.CancelledError:
cancellation_count += 1
cleanup_started.set()
await release.wait()
task = registry.create(worker(), owner="test.stubborn")
await started.wait()
try:
await registry.shutdown(timeout_seconds=0.001)
assert cleanup_started.is_set()
assert not task.done()
assert cancellation_count == 1
assert [record.owner for record in registry.records] == [
"test.stubborn"
]
assert reports[-1]["owners"] == ("test.stubborn",)
await registry.shutdown(timeout_seconds=0.001)
assert not task.done()
assert cancellation_count == 1
assert len(reports) == 1
release.set()
await task
await asyncio.sleep(0)
assert registry.records == ()
finally:
release.set()
loop.set_exception_handler(previous_handler)
asyncio.run(scenario())