Kotlin Coroutines with timeout

19,381

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.

Share:
19,381
guenhter
Author by

guenhter

Updated on July 21, 2022

Comments

  • guenhter
    guenhter almost 2 years

    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)
    }
    ...
    
  • vach
    vach over 5 years
    Roman is there something like d.await(timeout) such that it does not cancel but rather wakes up from current suspended function giving chance to check something and if needed do d.cancel()... i want d to keep working until cancelled manually
  • Roman  Elizarov
    Roman Elizarov over 5 years
    Use withTimeout(timeout) { d.await() } for that purpose - it does exactly that. async task is still working after timeout.
  • vach
    vach over 5 years
    I believe thats what i tried to do, if you have a minute please take a look at my attempt stackoverflow.com/questions/54370415/…
  • dugsmith
    dugsmith almost 2 years
    Here's the current location of the Cancellation & Timeouts section of the Coroutines Guide: kotlinlang.org/docs/cancellation-and-timeouts.html