fix(token_usage): replace threading.Lock with asyncio.Lock in record()#1893
Merged
zhijianma merged 1 commit intoagentscope-ai:mainfrom Mar 20, 2026
Merged
Conversation
TokenUsageManager.record() is an async method but used threading.Lock for _file_lock, which blocks the entire event loop under concurrent calls. Replace with asyncio.Lock and change `with` to `async with`. The class-level threading.Lock in get_instance() is kept as-is since that classmethod is synchronous and never awaits. Closes agentscope-ai#1834 This contribution was developed with AI assistance (Claude Code + Codex). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9 tasks
zhijianma
approved these changes
Mar 20, 2026
This was referenced Mar 20, 2026
Contributor
Author
|
Thanks for the review. |
tudan110
pushed a commit
to tudan110/QwenPaw
that referenced
this pull request
Apr 4, 2026
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.
Summary
TokenUsageManager.record()is anasyncmethod that usesself._file_lock = threading.Lock()to serialize file I/O.threading.Lockis a blocking primitive - when acquired inside an async method on the event loop thread, it blocks the entire event loop until released. Under concurrent calls this produces the deadlock captured in the py-spy stack dump in #1834.Fix: Replace
threading.Lock()withasyncio.Lock()for_file_lockand changewith self._file_lock:toasync with self._file_lock:. This is the same pattern already used inapp/runner/manager.py,app/crons/manager.py, andapp/mcp/manager.py.The class-level
threading.Lockinget_instance()is kept unchanged since that classmethod is synchronous and never awaits.Changes
src/copaw/token_usage/manager.py: Addimport asyncio, change_file_lockfromthreading.Lock()toasyncio.Lock(), changewithtoasync withinrecord()Test plan
ruff checkpassesrecord()calls from multiple coroutines no longer deadlockCloses #1834
This contribution was developed with AI assistance (Claude Code + Codex).