sickworm
sickworm

Reputation: 41

Kotlin coruntines wont execute when in a launch and callback

I thought I was familiar enough with Kotlin's coroutines, until I got this code.

1 to 8 are all printed except 2:

import kotlinx.coroutines.*
import java.lang.Runnable
import java.lang.Thread.sleep
import kotlin.concurrent.thread

fun main() {
    runBlocking {
        Client.createAccount()
        delay(1000)
    }
}

object Client: CoroutineScope {
    override val coroutineContext = newSingleThreadContext("Client")

    fun createAccount() = launch {
        Client2.init(Runnable {
            println('1')
            launch {
                println('2')
            }
            ok()
            ok2()
        })

        println('7')
        launch {
            println('8')
        }
    }

    fun ok() {
        println('3')
        launch {
            println('4')
        }
    }

    fun ok2() = launch {
        println('5')
        launch {
            println('6')
        }
    }
}

object Client2 {

    fun init(runnable: Runnable) = thread {
        sleep(100)
        runnable.run()
    }
}

The result is:

7
8
1
3
4
5
6

The coroutine in callback will never be called. Why? And if I remove the launch in createAccount() the 1 to 8 will be all printed. Also if I use GlobalScope.launch { println('2') } instead of launch { println('2') }, I can also get the 2 printed.

Upvotes: 3

Views: 892

Answers (2)

Choim
Choim

Reputation: 392

the reason is that anonymous class uses its wrapper scope as a parent.

launch { println('2') } in Runnable { } will be cancelled when parent job createAccount() launched is completed.

Therefore, it can't be invoked because it would be cancelled right after launch { println('8') }.

So, If you change Client like below, it would print '2' correctly.

object Client: CoroutineScope {
    override val coroutineContext = Dispatchers.Main

    fun createAccount() = launch {
        Client2.init(Run())

        println("7")
        launch {
            println("8")
        }
    }

    fun ok() {
        println("3")
        launch {
            println("4")
        }
    }

    fun ok2() = launch {
        println("5")
        launch {
            println("6")
        }
    }

    class Run: Runnable {
        override fun run() {
            println("1")
            launch {
                println("2")
            }
            ok()
            ok2()
        }
    }
}

Upvotes: 3

Naor Tedgi
Naor Tedgi

Reputation: 5707

launch posts a Runnable in a Handler, so its code execution is not immediate. launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) will immediately execute its lambda expression in the current thread.

change the dispatcher to the current one you are using change the lunch from inside the thread to

launch (coroutineContext, CoroutineStart.UNDISPATCHED)

.

fun createAccount() = launch {

    Client2.init(Runnable {
        println('1')
        launch (coroutineContext, CoroutineStart.UNDISPATCHED){
            println('2')
        }
        ok()
        ok2()
    })

    println('7')
    launch {
        println('8')
    }
}

output:

7
8
1
2
3
4
5
6

Upvotes: 0

Related Questions