kotlinkotlin-coroutines

Kotlin Coroutines with timeout


I'm currently writing a test-function which should run a block or (when a certain timeout is reached) throws an exception.

I was trying this with Coroutines in Kotlin but ended up with a mixture of Coroutines and CompletableFuture:

fun <T> runBlockWithTimeout(maxTimeout: Long, block: () -> T ): T {
    val future = CompletableFuture<T>()

    // runs the coroutine
    launch { block() }

    return future.get(maxTimeout, TimeUnit.MILLISECONDS)
}

This works, but I'm not sure if this is the intended way to solve that problem in kotlin.

I also tried other approaches:

runBlocking {
    withTimeout(maxTimeout) {
        block()
    }
}

But this seems not to work as soon as the block calls e.g. Thread.sleep(...)

So is the CompletableFuture approach the way to go or is there a better one?

update 1 What I want to achieve:

Async Integration-Test code (like receiving data from RabbitMq) should be tested somehow like this:

var rabbitResults: List = ... // are filled async via RabbitListeners
...
waitMax(1000).toSucceed {
    assertThat(rabbitResults).hasSize(1)
}
waitMax(1000).toSucceed {
    assertThat(nextQueue).hasSize(3)
}
...

Solution

  • withTimeout { ... } is designed to cancel the ongoing operation on timeout, which is only possible if the operation in question is cancellable.

    The reason it works with future.get(timeout, unit) is because it only waits with timeout. It does not actually cancel or abort in any way your background operation which still continues to execute after timeout had elapsed.

    If you want to mimick similar behavior with coroutines, then you should wait with timeout, like this:

    val d = async { block() } // run the block code in background
    withTimeout(timeout, unit) { d.await() } // wait with timeout
    

    It works properly because await is a cancellable function which you can verify by reading its API documentation.

    However, if you want to actually cancel the ongoing operation on timeout, then then you should implement your code in asyncronous and cancellable way. Cancellation is cooperative, so, to start, the underlying library that you are using in your code has to provide asynchronous API that supports cancellation of ongoing operation.

    You can read more about cancellation and timeouts in the corresponding section of the coroutines guide and watch the KotlinConf's Deep Dive into Coroutines on how to integrate coroutines with asynchronous libraries.