Repository navigation
test: keep observer alive through callback completion in GrainObserverWithGenericMethods - #11439
Conversation
…rWithGenericMethods Fixes a latent GC-lifetime race in a client-side observer test that produced a 10-minute CI hang (dotnet#11427). CreateObjectReference tracks its target via a WeakReference; once ValueTask is read, the test's local observer field is no longer used by the method body, so a GC running while the one-way SetValue call is still in flight could collect the observer before it is invoked. The dispatched message is processed to completion normally (admission gates open, no runtime deadlock), but the invocation silently finds a collected target, leaving the awaited TaskCompletionSource uncompleted forever. Add GC.KeepAlive, matching the existing pattern used elsewhere in the test suite for this exact hazard (GrainCallFilterTests, ObserverCancellationTokenTests).
There was a problem hiding this comment.
🟢 Approval recommended
The focused test-only fix addresses the lifetime hazard while preserving the existing callback assertion.
0 open findings
What changed in this PR
Fixes the observer-lifetime race behind #11427 without changing Orleans runtime behavior or callback verification.
Changes:
- Adds
GC.KeepAlive(localObject)after the callback assertion. - Documents why the observer must remain strongly reachable.
| File | Description |
|---|---|
| test/Orleans.DefaultCluster.Tests/CodeGenTests/GeneratorGrainTest.cs | Keeps the observer alive through callback completion to prevent premature collection and hangs. |
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Code coverage
Report-only conclusion: regressed. The current-main baseline is commit Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities. The comparison remains report-only while normal line and branch variance is calibrated. Coverage details |
Problem
GrainObserverWithGenericMethodsawaits a callback task after its final use of the observer object. Orleans object references track observer targets weakly, so the test must keep the observer strongly reachable through callback completion.The Linux net10.0 hang in #11427 exposed this lifetime gap (run 37592539156, job 112697233952, head
3c27db51on #11420). The retained dump contains the pending callback task and observer registration, but no survivingObserverWithGenericMethodsinstance. Runtime dispatch for the matchingSetValue("ToastedEnchiladas")message had completed; that establishes completion of dispatch handling rather than successful invocation of the observer method.Solution
Add
GC.KeepAlive(localObject)after callback completion and the result assertion. This retains the observer for the entire operation and follows the existing pattern inGrainCallFilterTestsandObserverCancellationTokenTests, preserving the runtime's established weak-reference lifetime contract.Rationale
The observer-lifetime requirement predates the recent networking changes. A contribution from #10074's delivery timing remains an unmeasured hypothesis; no recent introducing change has been established. The patch fixes the test's lifetime guarantee directly.
Closes #11427.