Ambiguous coroutineContext while calling co-routine builders (launch, async) in a suspended function

Viewed 701

I have a class Runner that implements the CoroutineScope interface as shown below. It has a suspended function called run. when I use the co-routine builder functions (launch, async) in this suspended run function I get the below warning

Ambiguous coroutineContext due to CoroutineScope receiver of suspend function

The Runner class has a single coroutineContext property implemented. Can someone explain the logic behind the warning message?

class Runner: CoroutineScope {

    override private val coroutineContext = Dispatchers.IO

    suspend fun run()  {
           val job1 = launch { delay(2000); println("launching job1") }
           val job2 = launch { delay(2000); println("launching job2") }
           listOf(job1, job2).forEach { it.join() }
    }

}
3 Answers

it is ambiguous due to the suspend modifier. Your run() function will be called from another couroutineScope. So the launch builders inside it can start a coroutine or suspend within an existing coroutine. That is the ambiguity. You can fix it by removing the suspend modifier:

class Runner : CoroutineScope {

    override val coroutineContext = Dispatchers.IO

    fun run() = launch {
            val job1 = launch { delay(2000); println("launching job1") }
            val job2 = launch { delay(2000); println("launching job2") }
            listOf(job1, job2).forEach { it.join() }
        }

}

you need to explicitly state that run() uses coroutineContext of Runner, that will remove ambiguity

suspend fun run() = withContext(coroutineContext) {
    val job1 = launch { delay(2000); println("launching job1") }
    val job2 = launch { delay(2000); println("launching job2") }
    listOf(job1, job2).forEach { it.join() }
}

alternatively you could use the context in which run is called if you add an argument, e.g.

suspend fun run(cc:CoroutineContext = coroutineContext) = withContext(cc) {
    val job1 = launch { delay(2000); println("launching job1") }
    val job2 = launch { delay(2000); println("launching job2") }
    listOf(job1, job2).forEach { it.join() }
}

try to avoid suspend function inside Coroutine Scoped class or function unless it would threw the above warning

class Runner: CoroutineScope {

            override private val coroutineContext = Dispatchers.IO

            suspend fun run()  {
                   val job1 = launch { delay(2000); println("launching job1") }
                   val job2 = launch { delay(2000); println("launching job2") }
                   listOf(job1, job2).forEach { it.join() }
            }

        }

to

class Runner: CoroutineScope {
            //also remove the `private` visibility modifier
            override val coroutineContext = Dispatchers.IO

            fun run()  {
                   val job1 = launch { delay(2000); println("launching job1") }
                   val job2 = launch { delay(2000); println("launching job2") }
                   this.launch{
                       listOf(job1, job2).forEach { it.join() }
                   }   
            }

        }
Related