Add Store.awaitSnapshotCapturable for race-free snapshot coordination#19
Merged
Conversation
StoreStatus.InHostImport (and Paused/WaitingForFuel) is published from
inside a gated execution segment, before the executing continuation
parks and StoreExecutionGate releases its mutex. An embedder that
observes status.first { it == InHostImport } on another thread and then
calls captureSnapshotState intermittently hits the fail-fast
tryAcquireCapture and gets "store execution has not parked". This exact
race made SnapshotSuspensionSafetyJvmTest flaky.
Reordering the publication is not an option: the gate release is
implicit in the ContinuationInterceptor machinery and only happens once
the segment actually suspends. Instead, expose a public awaitable
primitive: awaitSnapshotCapturable() suspends until a capturable status
is published AND the gate has been released (by briefly taking the gate
mutex), so a follow-up captureSnapshotState cannot fail with "has not
parked" unless the guest resumes in between - which it cannot while the
parked host import is still blocked.
Also document the publish-before-park ordering on StoreStatus and
captureSnapshotState, switch the previously flaky test to the new
primitive, and add a focused stress test for capture of a parked host
import from another thread. Supersedes the test-level dispatcher drain
on branch fix/snapshot-suspension-safety-jvm-test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve SnapshotSuspensionSafetyJvmTest in favor of the new Store.awaitSnapshotCapturable API: both the dispatcher drain that main used in the traversal test and the capture-probe polling loop in the cross-thread resumption test are superseded by awaiting the execution gate directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
Store.awaitSnapshotCapturable(), a public suspend primitive that returns only once a capturable status (Paused,WaitingForFuel,InHostImport) is published and the execution gate has been released. Documents the publish-before-park ordering onStoreStatusandcaptureSnapshotState, movesSnapshotSuspensionSafetyJvmTestonto the new primitive, and adds a stress test that captures a parked host import from another thread 32 times.Why
enterHostImportpublishesStoreStatus.InHostImportwhile the gated execution segment still holds the gate mutex; the mutex is released only when the host continuation actually parks. An embedder that observesstatus.first { it == InHostImport }on one thread and then callscaptureSnapshotStateintermittently loses the race against the still-running segment and getsSnapshotStateException("store execution has not parked...")— this is what madehostImportCannotResumeWhileSnapshotTraversalOwnsTheStoreflaky. Reordering the publication is not viable because the gate release is implicit in theContinuationInterceptormachinery, so the fix is an additive awaitable signal:awaitSnapshotCapturablebriefly takes the gate mutex, so after it returns a capture cannot fail with "has not parked" unless the guest resumes in between — which it cannot while the host import is still blocked.Supersedes the test-level dispatcher drain on branch
fix/snapshot-suspension-safety-jvm-test.Testing
./gradlew :core:jvmTest— 107 tests, 0 failures--tests '*SnapshotSuspensionSafety*'rerun 5x, green each time:core:compileKotlinMacosArm64— commonMain compiles for native🤖 Generated with Claude Code