Conversation
|
P2: Reentrant initial delivery still permits overlapping exclusive-resource ownership. Locations: This reproduces on both the PR and its base commit, so it is an unresolved gap, not a regression introduced here. It also limits the newly documented guarantee that the previous subscription is released before its replacement starts. Reproduction: an inner Suggested fix: serialize activation so a replacement selected during an in-progress |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Reentrant activation can still subscribe the replacement before the outgoing subscription has fully released its resource.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
What changed in this PR
Updates cache Switch subscription ownership to handle reentrant handoffs.
Changes:
- Adds generation-scoped subscription tracking and teardown ordering.
- Documents lifecycle behavior.
- Adds regression tests for reentrant switching and termination.
| File | Description |
|---|---|
ObservableCacheEx.Switch.cs |
Documents subscription lifecycle semantics. |
Cache/Internal/Switch.cs |
Implements generation-scoped handoffs. |
SwitchFixture.SubscriptionLifetime.cs |
Adds lifecycle regression tests. |
SwitchFixture.cs |
Makes the fixture partial. |
dynamicdata-cache.instructions.md |
Updates operator guidance. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Subscribe outside the gate and assign only to this generation's holder. If a | ||
| // synchronous notification selected a newer source, this holder is already disposed | ||
| // and disposes the late-returning subscription without touching the newer one. | ||
| subscription.Disposable = source.SubscribeSafe( |

Summary
Switchcould lose track of which inner subscription owned the current source when the outer observable re-entered during a reset, disposal, or the initial delivery of a newly selected source. A reentrant selection could activate a source that had already been superseded, or dispose the subscription belonging to the newest source, leaving the operator wired to a stale stream or to nothing at all.Generation-scoped ownership
Each selection of an inner source now carries its own generation identity. Activation, population, and teardown are all validated against the generation that initiated them, so a handoff triggered while an earlier handoff is still unwinding can no longer publish results from, or tear down, a subscription it does not own. A superseded generation quietly retires instead of racing the current one.
Exclusive-resource ordering
The subscription handoff is ordered so that the outgoing subscription is fully released before its replacement starts, rather than overlapping or being released afterwards by whichever path completed last. Outer completion is deferred until the selected inner source terminates, while errors and disposal cancel any activation still pending. The result is a single, unambiguous owner of the inner subscription at every point in the handoff.
Validation
Focused validation for this change was previously run and passed, covering the reentrant reset, disposal, and initial-delivery handoff paths along with the existing
Switchsuite.Fixes #1179