From 2f0b9eac7f937c818d00cad9c9c48c7924b258ae Mon Sep 17 00:00:00 2001 From: Ruslan Ibrahimau Date: Sun, 19 Jul 2026 10:03:59 +0300 Subject: [PATCH 1/2] Stabilize snapshot suspension safety test --- .../jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt b/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt index 1e356cf..88a007d 100644 --- a/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt +++ b/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt @@ -112,6 +112,10 @@ class SnapshotSuspensionSafetyJvmTest { try { val invocation = async(invocationDispatcher) { instance.invoke("wait") } store.status.first { it == StoreStatus.InHostImport } + // InHostImport is published before host code runs. Drain the + // single-thread dispatcher to prove that the host continuation + // has actually suspended and released the store execution gate. + withContext(invocationDispatcher) { Unit } val captureStarted = CountDownLatch(1) val finishCapture = CountDownLatch(1) From e1cf46ee0cb5af172ad6c62df233d7899d53e61b Mon Sep 17 00:00:00 2001 From: Ruslan Ibrahimau Date: Sun, 19 Jul 2026 11:47:11 +0300 Subject: [PATCH 2/2] Park the host import before completing it from the resumer thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI run 29678913141 showed suspendedHostImportMayResumeOnAnotherThread WithoutLosingGuestState resuming on an unexpected thread: InHostImport is published before the import's continuation actually parks, so a completion racing that window finishes the await without the intended thread hop. Poll Store.captureSnapshotState until it stops throwing — the capture gate can only be acquired once the running segment has suspended and released the store execution gate — before handing the result to the resumer thread, mirroring the dispatcher drain the other test already uses. Co-Authored-By: Claude Fable 5 --- .../wasm/core/SnapshotSuspensionSafetyJvmTest.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt b/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt index 88a007d..a055797 100644 --- a/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt +++ b/wasm-core/jvmTest/wasm/core/SnapshotSuspensionSafetyJvmTest.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.async +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -56,6 +57,16 @@ class SnapshotSuspensionSafetyJvmTest { instance.invoke("resume") } store.status.first { it == StoreStatus.InHostImport } + // InHostImport is published before the import's continuation + // parks. A capture probe only succeeds once the running segment + // has suspended and released the execution gate, so poll it to + // guarantee the completion below resumes a parked continuation + // on the resumer thread instead of finishing the await inline. + withTimeout(5_000) { + while (runCatching { store.captureSnapshotState(instance) }.isFailure) { + delay(1) + } + } assertFalse(invocation.isCompleted) withContext(resumerDispatcher) {