0
votes

After reading Kotlin documentation, I came up with the following code (which is not working - see below) to repeat a function call until it returns true, or timeout is reached.
I want to pause execution until this code block reaches timeout or success - it is not supposed to execute asynchronously.

Log.d(TAG, "This is the last line to be logged")
runBlocking {
    Log.d(TAG, "this line is never logged")
    try {
        withTimeout(timeoutMsL) {
            while ((isActive) && (!success)) {
                success = doSomething()
            }
        } 
    }
    catch (ex: TimeoutCancellationException) {
        Log.d(TAG, "this line is never logged either")
        doSomethingElse()
    }
}

timeoutMsL is a Long with typical value 50 ms. This code is called from C++ over the JNI. When I run it

  • nothing inside the runBlocking block runs
  • nothing after the runBlocking block runs
  • control returns to the C++ caller
  • there is an exception in the JNI, but the JNI doesn't log Kotlin or Java exception details.
  • no exception is logged in adb
  • when I tried surrounding the above code snippet with a try/catch/log block to catch Kotlin exceptions, nothing is logged

I have read that runBlocking should be avoided, but also you have to call withTimeout from an existing coroutine.
If I use a normal coroutine, execution of the calling function will continue before timeout /success is reached - I need to prevent this from happening.

How should this be coded in Kotlin?

1

1 Answers

1
votes

Your problem probably lies in doSomething(). Kotlin's coroutine implementation relies a lot on cooperative execution where child coroutines check flags to see if they have been cancelled (as withTimeout() would do). This would mean the outer coroutines will pause until they confirm the child coroutines have ended, blocking the entire function.

if doSomething never suspends and never checks if it is still active it will just run until completion regardless of the external situation.

To fix this, there are two options:

  1. Make doSomething() a suspend function and regularly suspend with either yield() or ensureActive() to respond to cancellation.
  2. Execute it on a dispatcher that is designed to interrupt normal blocking code like withContext(Dispatchers.IO).