Y G
Y G

Reputation: 143

How to dispatch coroutines directly to main thread on the JVM?

I'm setting up a kotlin coroutine based networking framework for the jvm. The Client and Server classes implement CoroutineScope, and the override for coroutinecontext is Dispatchers.IO, as I am pretty sure that's the correct Dispatcher to use for such a case. However, I wish to handle read packets on the main thread, or at least provide that option. Without reading the documentation, I used Dispatchers.Main, which I now realize is for the android UI thread. Is there a dispatcher I can use to get a coroutine running on the main thread? If not, how would I go about making one?

I have looked around the kotlin documentation on how to create a dispatcher based around a single thread, but I couldn't find anything besides newSingleThreadContext which creates a new thread. I also figured out that it is possible to create a dispatcher from a java Executor, but I'm still not sure how to limit this to a already existing thread.

class AbstractNetworkComponent : CoroutineScope {
    private val packetProcessor = PacketProcessor()
    private val job = Job()
    override val coroutineContext = job + Dispatchers.IO
}

class PacketProcessor : CoroutineScope {

    private val job = Job()
    override val coroutineContext = job + Dispatchers.Main //Android only!
    private val packetHandlers = mutableMapOf<Opcode, PacketHandlerFunc>()

    fun handlePacket(opcode: Opcode, packet: ReceivablePacket, networker: Writable) {
        launch(coroutineContext) {
            packetHandlers[opcode]?.invoke(packet, networker)
        }
    }
}

So with the Dispatchers.Main I get an IllegalStateException due to the android component missing. Is there a way to create a dispatcher that blocks the main thread until its completion (like runBlocking does?) Thanks!

Upvotes: 6

Views: 4452

Answers (2)

Sergio
Sergio

Reputation: 30645

As per Guide to UI programming with coroutines kotlinx.coroutines has three modules that provide coroutine context for different UI application libraries:

Also, UI dispatcher is available via Dispatchers.Main from kotlinx-coroutines-core and corresponding implementation (Android, JavaFx or Swing) is discovered by ServiceLoader API. For example, if you are writing JavaFx application, you can use either Dispatchers.Main or Dispachers.JavaFx extension, it will be the same object.

Upvotes: 4

Marko Topolnik
Marko Topolnik

Reputation: 200158

runBlocking is exactly what you need. It creates a dispatcher and sets it in the coroutine context. You can access the dispatcher with

coroutineContext[ContinuationInterceptor] as CoroutineDispatcher

and then you can pass it to an object that implements CoroutineScope or whatever else you want to do with it. Here's some sample code:

import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlin.coroutines.ContinuationInterceptor

fun main() {
    println("Top-level: current thread is ${Thread.currentThread().name}")
    runBlocking {
        val dispatcher = coroutineContext[ContinuationInterceptor]
                as CoroutineDispatcher
        ScopedObject(dispatcher).launchMe().join()
    }
}

class ScopedObject(dispatcher: CoroutineDispatcher) : CoroutineScope {
    override val coroutineContext = Job() + dispatcher

    fun launchMe() = launch {
        val result = withContext(IO) {
            "amazing"
        }
        println("Launched coroutine: " +
                "current thread is ${Thread.currentThread().name}, " +
                "result is $result")
    }
}

This will print

Top-level: current thread is main
Launched coroutine: current thread is main, result is amazing

Upvotes: 5

Related Questions