How to run functions in coroutines on after another

let’s say i have a blocking function which makes a heavy calculation
For example

fun cachedFib(x:Long):Long {
//check if value is cached 
// calculate if not in cache and populate cache
// return value
}

In a blocking world, i can run this function in a single thread pool executor and i am sure that two function calls are not happening in parallel.

Now let’s go to a non blocking coroutines world:

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.runBlocking

fun main() {
    val scope = CoroutineScope(newSingleThreadContext("aaa"))
    scope.launch { foo() }
    scope.launch { foo() }
    runBlocking {
        delay(2000)
    }

}

suspend fun foo() {
    println("looking in cache")
    println("making http call")
    delay(1000)
    println("after call")
}

The output is

looking in cache
making http call
looking in cache
making http call
after call
after call

This is correct because all about coroutines is not blocking but this means that if i have two parallel requests i am making two http calls, which is bad

I found a convoluted workaround which comes from recursions but i am sure that there is a better solution

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

fun main() {
    val scope = CoroutineScope(Dispatchers.IO)
    val deferred1  = get_next_async(scope)
    val deferred2 = get_next_async(scope)
    runBlocking {
        deferred1.await()
        deferred2.await()
    }

}
var sharedDeferred: Deferred<Int>? = null
@Synchronized
fun get_next_async(scope: CoroutineScope): Deferred<Int> {
    val deferredToWait = sharedDeferred
    sharedDeferred = scope.async {
        deferredToWait?.await()
        foo()
    }
    return sharedDeferred as Deferred<Int>
}


 suspend fun foo() {
    println("looking in cache")
    println("making http call")
    delay(1000)
    println("after call")
}


What is the idiomatic way to solve this issue?

scope.launch { foo(); foo() }
1 Like

Have you tried using a Mutex? See the documentation

Hi, I just called it twice as an toy example. It can be called multiple times from many places outside. The main issue is that i have to solve the “happens before” issue between coroutines execution and not in the coroutine itself which is designed as you showed.

I could do this. but this would mean that i block the threads. so this will revert the solution to be blocking. So for updating a counter this is more than fine, but for example to wait for a http call this iis not nice.

Using a Mutex does not block the thread. Read the documentation:

Mutual exclusion solution to the problem is to protect all modifications of the shared state with a critical section that is never executed concurrently. In a blocking world you’d typically use synchronized or ReentrantLock for that. Coroutine’s alternative is called Mutex. It has lock and unlock functions to delimit a critical section. The key difference is that Mutex.lock() is a suspending function. It does not block a thread.

2 Likes

Thanks @Varia ! I missed this.

For your specific use case, take a look here

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent-K-java.util.function.Function-