Repository navigation
Conversation
ToolManager.instances was a plain list, so every manager ever created (one per LLMAgent) stayed reachable forever. Long runs and batch sweeps accumulated dead managers, and add_tool_to_all() kept registering tools on all of them. Store instances in a weakref.WeakSet so a manager is collected once the application drops it. Iterating a WeakSet raises "Set changed size during iteration" if another thread creates a manager meanwhile (e.g. agents spawned during a threaded step), which a list never did; add_tool_to_all() therefore snapshots the set under a lock and registers outside it. Fixes mesa#336
Contributor
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
1 task done
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pre-PR Checklist
Summary
ToolManager.instancesheld a strong reference to every manager ever created (one perLLMAgent), so none could be garbage-collected. This PR makes the registry weak and keepsadd_tool_to_all()safe under concurrent construction, which a bareWeakSetdoes not.Bug / Issue
Fixes ToolManager instances are retained after they are no longer referenced #336.
Measured on
main: after building and discarding 20 models × 25 agents, 500 managers were still registered (agents and models themselves were freed). Memory grows without bound across long runs and batch sweeps, and everyadd_tool_to_all()call also iterates all dead managers.Implementation
instancesis now aweakref.WeakSet, so managers are released as soon as the application drops them.threading.Lockguardsadd()and a snapshot of the set inadd_tool_to_all(). Registration then runs on the snapshot, outside the lock.Why not only a
WeakSet(as in #337): iterating aWeakSetwhile another thread adds to it raisesRuntimeError: Set changed size during iteration. The old list never raised this, so a bareWeakSetswaps a leak for a crash. This is reachable when agents are created during a threaded step (step_agents_multithreaded) while a tool is broadcast.Why the lock is released before
register():threading.Lockis not re-entrant, so holding it while registering would deadlock if registration ever constructs a manager.Alternatives considered:
weakref.refwith removal callbacks: callbacks fire during GC, possibly mid-iteration, and mutating a list during iteration silently skips entries.Testing
New regression tests in
tests/test_tools/test_tool_manager.py:mainWeakSettest_unreferenced_manager_is_garbage_collectedtest_add_tool_to_all_skips_collected_managerstest_add_tool_to_all_tolerates_manager_created_mid_broadcastRuntimeErrorWeakSetraisesRuntimeError; this PR runs clean.pre-commit runpasses.Additional Notes
instanceskeeps the operations used in the codebase (len,in, iteration,.clear())..append()and indexing are no longer available; nothing in the repo uses them.WeakSetis unordered. Broadcast order has no effect because eachregister()is independent.WeakSet; this PR additionally covers the iteration race that change introduces. Happy to consolidate with Fix ToolManager instance retention #337 in whatever way maintainers prefer.