Rob Elliot
08/15/2025, 9:16 AMThread.interrupt()
by checking Thread.currentThread().isInterrupted
.
Migrating that to currentCoroutineContext().isActive
seemed fairly easy, but higher up the tree, and outside coroutine context, I now need to launch a coroutine and cancel it when the launching thread is interrupted, to maintain the contract.
I've tried this:
fun run() = runBlocking {
val deferred = async(start = LAZY) {
login.login()
}
try {
deferred.await()
} catch (_: InterruptedException) {
deferred.cancel()
deferred.await()
}
}
but my tests are failing in a way that shows that the contract has changed - indeed, it looks like the InterruptedException is not caught by that catch block at all because the lambda passed to runBlocking
is being run in a different thread to fun run()
. But I can't get a reference to deferred
outside runBlocking
! How are coroutines and threads meant to interop for cancellation?Joffrey
08/15/2025, 9:22 AMrunBlocking
propagate interruption as cancellation by default? Like doesn't it work if you just run your code in runBlocking
directly?Rob Elliot
08/15/2025, 9:23 AMRob Elliot
08/15/2025, 9:24 AMRob Elliot
08/15/2025, 9:28 AM"failure event is triggered when cancelled" {
// when
var exitStatus: ExitStatus? = null
val thread = thread { exitStatus = command.run() }
// and
thread.interrupt()
// and
thread.join()
// then
exitStatus shouldBe CANCELLED
}
If I make command.run()
just:
fun run() = runBlocking {
login.login().exitStatus
}
I get java.lang.AssertionError: Expected CANCELLED but actual was null
as the test failure, and also see this exception printed to stderr:
Exception in thread "Thread-0" java.lang.InterruptedException
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:98)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:70)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:48)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at wiremock.cli.core.authentication.LoginCommand.run(LoginCommand.kt:24)
at wiremock.cli.core.authentication.LoginCommandSpec$1$11.invokeSuspend$lambda$0(LoginCommandSpec.kt:405)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
ross_a
08/15/2025, 9:32 AMrunInterruptible
ross_a
08/15/2025, 9:33 AMephemient
08/15/2025, 2:00 PMasync { login.login() }.asCompletableFuture().get().exitStatus
or something like that? https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.future/as-completable-future.html is supposed to propagate cancellation properlyephemient
08/15/2025, 2:06 PMfun run() = future { login.login() }.get()
Rob Elliot
08/15/2025, 2:31 PMlouiscad
08/16/2025, 7:49 AMcoroutineScope { … }