Skip to content

Commit 1fed80e

Browse files
committed
fix for windows tests
1 parent 137855d commit 1fed80e

2 files changed

Lines changed: 22 additions & 8 deletions

File tree

src/ezmsg/core/backend.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,16 @@ def _shutdown_was_unclean(self) -> bool:
462462
return main_shutdown_errors or loop_unclean
463463

464464
def _exit_with_sigint(self) -> None:
465+
code = 0xC000013A if os.name == "nt" else 130
466+
if os.name == "nt":
467+
os._exit(code)
468+
465469
prev_handler = None
466470
try:
467471
prev_handler = signal.getsignal(signal.SIGINT)
468472
signal.signal(signal.SIGINT, signal.SIG_DFL)
469473
signal.raise_signal(signal.SIGINT)
470474
except Exception:
471-
code = 0xC000013A if os.name == "nt" else 130
472475
raise SystemExit(code)
473476
finally:
474477
if prev_handler is not None:
@@ -477,7 +480,6 @@ def _exit_with_sigint(self) -> None:
477480
except Exception:
478481
pass
479482

480-
code = 0xC000013A if os.name == "nt" else 130
481483
raise SystemExit(code)
482484

483485
def _cleanup(self) -> None:

src/ezmsg/core/backendprocess.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ def _strict_shutdown_enabled() -> bool:
4444
class ShutdownSummary:
4545
cancelled_tasks: int = 0
4646
executor_active: int = 0
47+
pending_tasks: int = 0
4748
suppressed_errors: int = 0
4849
forced_interrupt: bool = False
4950

5051
@property
5152
def unclean(self) -> bool:
5253
return bool(
5354
self.executor_active
55+
or self.pending_tasks
5456
or self.suppressed_errors
5557
or self.forced_interrupt
5658
)
@@ -613,36 +615,45 @@ def _loop_exception_handler(
613615
if not strict_shutdown:
614616
shutdown_suppress.set()
615617
# Cancel and await remaining tasks before stopping the loop.
616-
async def _cancel_remaining() -> int:
618+
async def _cancel_remaining(timeout: float = 1.0) -> tuple[int, int]:
617619
tasks = [
618620
t
619621
for t in asyncio.all_tasks()
620622
if t is not asyncio.current_task() and not t.done()
621623
]
622624
for t in tasks:
623625
t.cancel()
624-
if tasks:
625-
await asyncio.wait(tasks)
626-
return len(tasks)
626+
if not tasks:
627+
return 0, 0
628+
_, pending = await asyncio.wait(tasks, timeout=timeout)
629+
return len(tasks), len(pending)
627630

628631
cancelled_count = 0
632+
pending_count = 0
629633
forced_interrupt = False
630634
fut = asyncio.run_coroutine_threadsafe(_cancel_remaining(), loop)
631635
try:
632-
cancelled_count = fut.result()
636+
cancelled_count, pending_count = fut.result()
633637
except KeyboardInterrupt:
634638
forced_interrupt = True
635639
fut.cancel()
636640
except Exception:
637641
cancelled_count = 0
642+
pending_count = 0
638643

639644
suppressed_count = suppressed_shutdown_errors["count"]
640-
if cancelled_count or suppressed_count or forced_interrupt:
645+
if cancelled_count or suppressed_count or forced_interrupt or pending_count:
641646
if forced_interrupt and not cancelled_count and not suppressed_count:
642647
logger.warning(
643648
"Shutdown interrupted; tasks may still be running. "
644649
"Re-run with EZMSG_STRICT_SHUTDOWN=1 to debug tasks with poor shutdown behavior."
645650
)
651+
elif pending_count:
652+
logger.warning(
653+
"Shutdown timed out waiting for %d task(s). "
654+
"Re-run with EZMSG_STRICT_SHUTDOWN=1 to debug tasks with poor shutdown behavior.",
655+
pending_count,
656+
)
646657
else:
647658
logger.warning(
648659
"Shutdown suppressed %d error(s) and cancelled %d task(s). "
@@ -654,6 +665,7 @@ async def _cancel_remaining() -> int:
654665

655666
if shutdown_summary is not None:
656667
shutdown_summary.cancelled_tasks = cancelled_count
668+
shutdown_summary.pending_tasks = pending_count
657669
shutdown_summary.executor_active = (
658670
executor.active_count() if executor is not None else 0
659671
)

0 commit comments

Comments
 (0)