Skip to content

Commit 42ce46c

Browse files
committed
Improve documentation and behavior of timeout semantics in bg_tasks
1 parent 1a987ca commit 42ce46c

File tree

2 files changed

+19
-3
lines changed

2 files changed

+19
-3
lines changed

src/async_utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
__author__ = "Michael Hall"
1010
__license__ = "Apache-2.0"
1111
__copyright__ = "Copyright 2020-Present Michael Hall"
12-
__version__ = "2025.07.08b"
12+
__version__ = "2025.07.11b"
1313

1414
import os
1515
import sys

src/async_utils/bg_tasks.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from __future__ import annotations
1616

1717
import asyncio
18+
import time
1819
from collections.abc import Coroutine
1920
from contextvars import Context
2021

@@ -48,7 +49,10 @@ class BGTasks:
4849
Parameters
4950
----------
5051
exit_timeout: int | None
51-
Optionally, the number of seconds to wait before timing out tasks.
52+
Optionally, the number of seconds to wait before sending a
53+
cancellation to pending tasks. This does not guarantee that after
54+
the timeout the context manager will exit immediately,
55+
as tasks may catch cancellation.
5256
5357
In applications that care about graceful shutdown, this should
5458
usually not be set. When not provided, the context manager
@@ -99,8 +103,20 @@ async def __aenter__(self: t.Self) -> t.Self:
99103
return self
100104

101105
async def __aexit__(self, *_dont_care: object) -> None:
106+
start = time.monotonic()
107+
pending: set[asyncio.Task[t.Any]] = set()
102108
while tsks := self._tasks.copy():
109+
if self._etime:
110+
max_sleep = start - time.monotonic() + self._etime
111+
if max_sleep <= 0:
112+
break
113+
103114
_done, pending = await asyncio.wait(tsks, timeout=self._etime)
115+
await asyncio.sleep(0)
116+
117+
if pending:
104118
for task in pending:
105119
task.cancel()
106-
await asyncio.sleep(0)
120+
# This wait is required in case tasks catch cancelation and
121+
# do further cleanup
122+
await asyncio.wait(pending)

0 commit comments

Comments
 (0)