Coroutines Help - async, withTimeout which stops blocking the thread once it runs out of time

I’m trying to make a function which triggers a possibly slow operation which can’t be cancelled. I want this operation to run in a coroutine with a timeout. Since the operation cannot be cancelled as mentioned before, I need the function to return after the time out, but the operation to stay in the background.

The code I have been trying to get working runs a lengthy operation of 10 seconds asynchronously which has a time out of 5 seconds, so therefore the function should return after the timeout and let main continue its job, printing “foo execution finished”, and finally 5 more seconds later the slow job would print “job ends (10 seconds passed)”.

Here’s the code:

fun main() {
    println("program execution begins")
    foo()
    println("foo execution finished")
    while(true);
}

fun foo() = runBlocking {
    val job = async {
    val endTimeMillis = System.currentTimeMillis() + (10 * 1000)

    while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
        println("job ends (10 seconds passed)")
    }

    try {
        withTimeout(5000) {
            println("start awaiting with 5 secs timeout")
            job.await()
        }
    } catch (ex: TimeoutCancellationException) {
        println("out of time")
    }
}

Which then produces the following result:

program execution begins
start awaiting with 5 secs timeout
job ends (10 seconds passed)
out of time
foo execution finished

But this is not exactly the behavior I need in this case as mentioned before. I need to make it so that output looks something like:

program execution begins
start awaiting with 5 secs timeout
out of time
foo execution finished
job ends (10 seconds passed)

In addition to this, I can’t use any sort of “kotlin-coroutines” function in the async to archive this behavior, since the code called in there will be user code unrelated to the coroutine, possibly written in Java. hence the while loop for blocking the async block instead of a delay() in the sample.

Thanks in advance for the help!

runBlocking creates a Dispatcher that uses the current thread. This is why withTimeout doesn’t work at all. The blocking code is blocking any other coroutine from resuming. Even if this weren’t the case, foo() would still wait for the job to finish due to Structured concurrency.

GlobalScope can be used to launch coroutines that are not associated to the current CoroutineScope and will not be constrained by Structured Concurrency.

To ensure that any blocking code is not blocking other coroutines from resuming, always use Dispatchers.IO or your own dedicated Dispatcher when running blocking code.

Note that async is for returning a result while launch is for starting work strictly for side-effects. In this sample, at least, `launch would be more appropriate.

So your sample can be simply “fixed” by changing two lines:
val job = async {val job = GlobalScope.launch(Dispatchers.IO) {)
job.await()job.join()

I’d be careful of using GlobalScope. Having code running in the background without limit or tracking can easily cause bugs or at the least excessive resource consumption. You can limit this by using a custom Dispatcher built off a thread limited Executor (see asCoroutineDispatcher) or track active jobs with a data structure.