fix: Thread-safe lock dictionary access in completion_block.py#57
fix: Thread-safe lock dictionary access in completion_block.py#57ivosetyadi wants to merge 1 commit intobagofwords1:mainfrom
Conversation
Fixed race condition in global _block_locks dictionary by wrapping setdefault() calls with threading.Lock. This ensures atomic lock creation and prevents multiple coroutines from creating different locks for the same block_id in high-concurrency scenarios. Changes: - Added threading.Lock (_block_locks_lock) to protect dictionary access - Wrapped both setdefault() calls (lines 112-113, 174-175) with lock - Ensures only one coroutine can check/modify _block_locks at a time This prevents the race condition where: 1. Coroutine A checks if block_id exists (not found) 2. Coroutine B checks if block_id exists (not found) 3. Coroutine A creates Lock1 and stores it 4. Coroutine B creates Lock2 and overwrites Lock1 5. Two different locks now exist, defeating synchronization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
@claude review |
1 similar comment
|
@claude review |
|
try now @claude review |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
@claude please review |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
please review @claude |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Pull request overview
This PR addresses a race condition in the _block_locks dictionary by introducing thread-safe access using a threading.Lock. The fix ensures that lock creation for each block_id is atomic, preventing multiple coroutines from creating different locks for the same block.
Key Changes
- Added
threading.Lockto protect dictionary operations on_block_locks - Wrapped both
setdefault()calls insend_completion_blocks_to_slack()and_send_block_to_slack()with the new lock - Maintains the existing async behavior while ensuring thread-safe dictionary access
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| _sent_block_text_ids = set() | ||
| _sent_block_tool_ids = set() |
There was a problem hiding this comment.
The global sets _sent_block_text_ids and _sent_block_tool_ids are accessed by multiple coroutines without synchronization protection. While the per-block asyncio.Lock prevents race conditions for the same block_id, different block_ids can execute concurrently and simultaneously modify these shared sets.
Python sets are not thread-safe for concurrent modifications. When multiple coroutines for different blocks execute concurrently, they can simultaneously call .add() on these sets (lines 121, 136, 206, 224) or check membership with in, potentially corrupting the internal set structure.
Consider protecting these set operations with _block_locks_lock or a dedicated lock to ensure thread-safe access to these shared data structures.
Summary
Fixed race condition in global
_block_locksdictionary by wrappingsetdefault()calls withthreading.Lockto ensure atomic lock creation. This prevents multiple coroutines from creating different locks for the sameblock_idin high-concurrency scenarios.Problem
The global
_block_locksdictionary usedsetdefault()without protection at lines 109 and 170 incompletion_block.py. This created a race condition:setdefault()performs check-then-act in two stepsasyncio.Lock()instances for the same block_idScenario:
Solution
Added
threading.Lock(_block_locks_lock) to protect all dictionary access:Changes
import threading_block_locks_lock = threading.Lock()to protect dictionary accesssetdefault()call insend_completion_blocks_to_slack()setdefault()call in_send_block_to_slack()Why threading.Lock and not asyncio.Lock?
setdefault())threading.Lockensures atomicity across coroutines without async overheadasyncio.Lockstored in the dictionary handles the actual async workImpact
Testing
python -m py_compile)Related Issues
Part of security audit issue #6: Race Conditions
🤖 Generated with Claude Code