Skip to content

Break the DispatchSession ↔ worker-task reference cycle to reduce per-dispatch GC pressure — Closes #266#272

Merged
conradbzura merged 2 commits into
wool-labs:masterfrom
conradbzura:266-break-worker-task-ref-cycle
Jul 2, 2026
Merged

Break the DispatchSession ↔ worker-task reference cycle to reduce per-dispatch GC pressure — Closes #266#272
conradbzura merged 2 commits into
wool-labs:masterfrom
conradbzura:266-break-worker-task-ref-cycle

Conversation

@conradbzura

Copy link
Copy Markdown
Contributor

Summary

Break the DispatchSession → _worker_task → coro → session reference cycle so a completed dispatch's session, worker task, and per-fork contexts are reclaimed promptly by refcounting instead of lingering until the next cyclic-GC pass. The worker driver's _run coroutine closes over the session, so the session's strong reference to its worker task formed a cycle only the cyclic collector could reclaim; under sustained dispatch these accumulate between GC passes (~40× more live tasks / ~14× more contexts without forced GC). Clear self._worker_task in the task's done-callback to sever it. This is safe: the only post-completion reader, cancel(), already guards on the worker task being non-None, and teardown (drain/__aexit__) synchronizes on _worker_done, not _worker_task. Closes #266

Proposed changes

Clear the worker-task reference on completion (runtime/worker/session.py)

In DispatchSession._schedule_worker's _on_done done-callback, set self._worker_task = None after settling the completion future. This drops the session's strong reference to the finished worker task, so session ↔ _worker_task ↔ coro is no longer a cycle and refcounting reclaims the objects immediately.

Test cases

# Test Suite Given When Then Coverage Target
1 test_session A coroutine dispatch driven to natural (non-cancelled) completion The dispatch finishes and the worker task's done-callback runs The captured worker task is reclaimed (public weakref clears) with automatic GC disabled Cycle severed on completion
2 test_session A completed dispatch whose session is deliberately retained Automatic cyclic GC is disabled and no gc.collect() is forced A weakref to the finished worker task still clears — refcounting reclaims it, not the retained session pinning it Refcount reclamation, not GC

Note: the tests identify the worker driver task via the private _run coroutine name — there is no public discriminator between it and in-flight step tasks on the worker loop (review finding B2, retained as the pragmatic last resort).

@conradbzura conradbzura self-assigned this Jul 2, 2026
@conradbzura conradbzura marked this pull request as ready for review July 2, 2026 15:38
The worker driver task runs a coroutine that closes over its
DispatchSession, and the session holds the task in _worker_task, so the
session, its task, and the task's forked contexts form a reference
cycle that only the cyclic collector can reclaim. Under sustained
dispatch these accumulate between collections.

Clear _worker_task in the task's done-callback once the completion
future is settled, breaking the cycle so reference counting reclaims
the session and task as soon as the dispatch ends. The only reader
after completion, cancel, already tolerates a None task, and teardown
synchronizes on the separate completion future, so dropping the
reference is safe.
Drive a coroutine dispatch to natural completion and assert the worker
driver task is reclaimed, observed through a weakref that clears with
automatic collection disabled, so reference counting rather than a
cyclic pass frees it. Retain the finished session explicitly to prove
it no longer pins the task. The driver task is identified through its
coroutine name, as no public handle distinguishes it from an in-flight
step task on the worker loop.
@conradbzura conradbzura force-pushed the 266-break-worker-task-ref-cycle branch from 760955e to 7759fd0 Compare July 2, 2026 23:01
@conradbzura conradbzura merged commit 23e5cca into wool-labs:master Jul 2, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Break the DispatchSession ↔ worker-task reference cycle to reduce per-dispatch GC pressure

1 participant