Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to suspend a coroutine at a specific point

I am at loss with the following problem.

I have the following code:

val parentJob: Job = Job()

launch(parent = parentJob) {
    while (true)
    {
        if (!parentJob.isCompleted)
        {
            // I want to control suspension here
            println("Resumed")
        }
    }
}

I would like to be able to control, somehow akin to a semaphore, when should the coroutine suspend and when to resume exactly in the commented part of the snippet

I know there's suspendCancellableCoroutine but I am unsure how to use it or if it is appropriate here

How can this be achieved or are there any tutorials about this?

like image 360
IS1_SO Avatar asked Dec 24 '22 05:12

IS1_SO


1 Answers

It would be more helpful to think about coroutines in terms of callbacks and continuations, not threads and semaphores.

Internally, when the coroutine is suspended, the entire chain of suspend fun calls returns with a special internal object, COROUTINE_SUSPENDED. So the whole execution of a coroutine is just a function call during which a callback object is being built up. This callback is the continuation and when you call it, execution resumes from the place which returned the special COROUTINE_SUSPENDED object.

Kotlin runs the block you pass to suspendCancellableCoroutine with the continuation object as the parameter. So you should save this object and make it available to the code outside the coroutine.

Here's some code that may help your understanding. Note there's no need to create a separate parent job if you want to cancel the coroutine. You can just cancel the Job instance that launch returns and the coroutine will not resume after suspension.

import kotlin.coroutines.*
import kotlinx.coroutines.*

var continuation: Continuation<String>? = null

fun main(args: Array<String>) {
    val job = GlobalScope.launch(Dispatchers.Unconfined) {
        while (true) {
            println(suspendHere())
        }
    }

    continuation!!.resume("Resumed first time")
    continuation!!.resume("Resumed second time")
    job.cancel()
    continuation!!.resume("This shouldn't print")
}

suspend fun suspendHere() = suspendCancellableCoroutine<String> {
    continuation = it
}

If you still need an explicit check (because there aren't enough suspension points on the execution path), you can just use the isActive property which is available directly to the block:

while (isActive) ...
like image 125
Marko Topolnik Avatar answered Dec 28 '22 06:12

Marko Topolnik