MonitoringService: fix SetKernel/CollectAsync race with lock-free state snapshot#35
Conversation
Co-authored-by: casuffitsharp <78829483+casuffitsharp@users.noreply.github.com>
… in MonitoringService Co-authored-by: casuffitsharp <78829483+casuffitsharp@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a race condition in MonitoringService between SetKernel() (which could clear and re-initialize shared state dictionaries) and CollectAsync() (which reads that same shared state concurrently). The fix introduces a KernelState inner class that bundles the kernel reference with all associated per-tick state, then publishes it as a single volatile reference swap, so CollectAsync always operates on a coherent snapshot.
Changes:
- Added a
KernelStateinner class bundlingKernel, all_prev*dicts, and IO tracking collections into one object. - Replaced eleven separate mutable instance fields with a single
private volatile KernelState _state;SetKernelbuilds the new state off the hot path and publishes it atomically. - Updated
CollectAsyncand allCollect*/SubscribeDevice/OnIoRequestCompletedmethods to passKernelState stateas a parameter, making event handlers close over their creation-time state.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: casuffitsharp <78829483+casuffitsharp@users.noreply.github.com>
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.



SemaphoreSlim _kernelLockand all associated lock/acquire/release code_kernel+ all_prev*+ device-state into a single innerKernelStateclassvolatile KernelState _stateSetKernel(): build newKernelStatefully, then atomically assign_state = newState(no blocking, no locks)CollectAsync(): capture_stateonce per tick into a local; use it throughout — no lock neededSubscribeDevice/OnIoRequestCompletedclose over their ownKernelState— IO events always go to the correct stateSetKernel: publish_state = newStatebeforeClear()calls soCollectAsyncsees the new state before metrics are wipedOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.