In this official documentation, it says that coroutineScope just suspends, releasing the underlying thread for other usages.
But in example code, it looks it blocks the next process because println("Coroutine scope is over") is not executed until finishing the code block in coroutineScope.
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
    launch { 
        delay(200L)
        println("Task from runBlocking")
    }
    
    coroutineScope { // Creates a coroutine scope
        launch {
            delay(500L) 
            println("Task from nested launch")
        }
    
        delay(100L)
        println("Task from coroutine scope") // This line will be printed before the nested launch
    }
    
    println("Coroutine scope is over") // This line is not printed until the nested launch completes
}
I know runBlocking waits until all its children to complete. So in my understanding, runBlocking should wait for coroutineScope to complete after executing println("Coroutine scope is over") line.
Why does coroutineScope block the thread in this example?
coroutineScope suspended CoroutineScope scope, so the line "println("Coroutine scope is over") " would be executed after the previous coroutineScope completed. You can see the code of coroutineScope function :
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
contract {
    callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
    val coroutine = ScopeCoroutine(uCont.context, uCont)
    coroutine.startUndispatchedOrReturn(coroutine, block)
}}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With