Files
hermes-agent/cron/scheduler_detached_worker.py
kshitijk4poor 40488a4e54 refactor(cron): detached-worker teardown as a sibling module, no guards around a real Future
The deferral helpers from the previous commit were appended to the cron/scheduler.py facade and
carried ~70 lines of fallbacks (getattr/callable checks, result() waits, a teardown_registered
flag) for hypothetical Future doubles; the only producer is _cron_pool.submit, always a real
concurrent.futures.Future whose done()/add_done_callback() cannot raise. Also drops the
_finalize_cron_session_db passthrough. Behaviour unchanged; the test now asserts the
finalize+teardown contract on the real Future instead of a patched wrapper.
2026-09-06 22:56:51 +05:30

35 lines
1.3 KiB
Python

"""Cron: teardown of a worker that outlived its ``run_job``.
``ThreadPoolExecutor.shutdown(wait=False)`` after an inactivity timeout does not stop a
worker already inside ``run_conversation``. Finalizing its SessionDB from ``run_job``'s
``finally`` would close a handle the worker is still writing to — the checkpoint/WAL-unlink
overlap behind #102827. The worker's Future owns the teardown instead.
"""
from __future__ import annotations
import concurrent.futures
from typing import Optional
def defer_teardown_to_running_worker(
future: Optional[concurrent.futures.Future], session_db, agent, job_id: str, job_name: str,
cron_session_id: str,
) -> bool:
"""Return True when the worker is still running and its Future will finalize the session
and tear the agent down on completion; False when the caller must do it now."""
if future is None or future.done():
return False
from cron.scheduler import _finalize_cron_session, _teardown_cron_agent
def _finish(_future) -> None:
try:
if session_db:
_finalize_cron_session(session_db, agent, job_id, job_name, cron_session_id)
finally:
_teardown_cron_agent(agent, job_id)
# Runs inline if the worker finished between done() and here — still exactly once.
future.add_done_callback(_finish)
return True