Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to suspend a coroutine with a timeout?

What I want is a function like this:

suspendCoroutineWithTimeout(timeout: Long, unit: TimeUnit, crossinline block: (Continuation<T>) -> Unit)

That does basically the same thing as the existing suspendCoroutine function but if the callback or whatever was provided in the block dosen't get called within the specified timeout the corutine continues but with a TimeoutException or something like that.

like image 290
usbpc102 Avatar asked Feb 25 '18 14:02

usbpc102


3 Answers

You can combine withTimeout and suspendCancellableCoroutine in a straightforward way for the desired effect:

suspend inline fun <T> suspendCoroutineWithTimeout(
    timeout: Long, unit: TimeUnit,
    crossinline block: (Continuation<T>) -> Unit
) = withTimeout(timeout, unit) {
    suspendCancellableCoroutine(block = block)
}
like image 200
Roman Elizarov Avatar answered Oct 21 '22 00:10

Roman Elizarov


Perfect answer from @Roman Elizarov.. Just adding my 2 cents on it because I needed a return from that call.. So adding T? return it would be ...

suspend inline fun <T> suspendCoroutineWithTimeout(timeout: Long, crossinline block: (Continuation<T>) -> Unit ) : T? {
    var finalValue : T? = null
    withTimeoutOrNull(timeout) {
        finalValue = suspendCancellableCoroutine(block = block)
    }
    return finalValue
}
like image 31
febaisi Avatar answered Oct 21 '22 01:10

febaisi


If you're using suspendCoroutine, that means you have full control over what you do with the continuation you got. For example, you can pass it to the callback-based async API and, additionally, to a scheduled task that will resume it with exception:

suspend fun mySuspendFun(timeout: Long): String {
    val didResume = AtomicBoolean()
    fun markResumed() = !didResume.getAndSet(true)

    return suspendCoroutine { cont ->
        launch(CommonPool) {
            delay(timeout)
            if (markResumed()) {
                cont.resumeWithException(TimeoutException())
            }
        }
        // call Async API, and in the callback, use
        //    if (markResumed()) {
        //        cont.resume(result)
        //    }
    }
}

However, Kotlin's standard library supports your use case first-class, as described in Roman Elizarov's answer. I suggest you use that approach in your project.

like image 2
Marko Topolnik Avatar answered Oct 21 '22 01:10

Marko Topolnik