Skip to content

Thread-safe MessageBus collections + perf#33

Open
newky2k wants to merge 3 commits into
mainfrom
development
Open

Thread-safe MessageBus collections + perf#33
newky2k wants to merge 3 commits into
mainfrom
development

Conversation

@newky2k

@newky2k newky2k commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Make the handler and log-listener collections thread-safe. They derive from Collection<T> (not thread-safe), so Post iterating while Subscribe/Unsubscribe mutate — the default path under RunPostOnSeperateTask or PostAsync — could throw "Collection was modified" or read torn state.
  • Add a SyncRoot lock to both collections; guard all iteration and snapshot the result so handlers run outside the lock (no reentrancy deadlock when a handler re-subscribes or posts).
  • Lock the service's compound check-then-act Subscribe/Unsubscribe/StopListening ops on the same root.
  • LogListernersCollection.FindAll now snapshots eagerly (was lazy LINQ enumerating after the lock released).
  • Optimise HandlersForEvent: drop per-handler ToLower allocations for an OrdinalIgnoreCase compare; single pass, no result allocation when nothing matches.

Tests

  • New ConcurrencyTest: parallel Subscribe/Unsubscribe/Post (10 DataRow repeats) + parallel-post delivery count.
  • Full suite green: 24/24.

🤖 Generated with Claude Code

newky2k and others added 3 commits June 13, 2026 17:16
The handler and log-listener collections derive from Collection<T>, which
is not thread-safe. Post iterates the collection while Subscribe/Unsubscribe
mutate it, so concurrent use (the default path when RunPostOnSeperateTask is
set or via PostAsync) could throw "Collection was modified" or read torn state.

- Add a SyncRoot lock to both collections; guard all iteration and snapshot
  the result so handlers are invoked outside the lock (avoids reentrancy
  deadlock when a handler re-subscribes or posts).
- Lock the service's compound check-then-act Subscribe/Unsubscribe/StopListening
  operations on the same root.
- Make LogListernersCollection.FindAll snapshot eagerly (was lazy LINQ that
  enumerated after the lock released).
- Optimise HandlersForEvent: drop per-handler ToLower allocations for
  OrdinalIgnoreCase compare, single-pass with no result alloc when no match.
- Add ConcurrencyTest covering parallel Subscribe/Unsubscribe/Post (10 DataRow
  repeats) and parallel-post delivery count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant