Few cents about my commits

Kotlin coroutines: Dispatchers.Main context for RoboVM applications

|

Android has kotlinx-coroutines-android Dispatcher.Main that coroutine execution on main/ui thread.
It allows writing an effective suspend logic on main thread as described on the usage page:

fun setup(hello: Text, fab: Circle) {
    GlobalScope.launch(Dispatchers.Main) { // launch coroutine in the main thread
        for (i in 10 downTo 1) { // countdown from 10 to 1 
            hello.text = "Countdown $i ..." // update text
            delay(500) // wait half a second
        }
        hello.text = "Done!"
    }
}

Overview

To make Dispatchers.Main available to kotlinx following steps to be done:

Approaches

Implementation is a port of kotlinx-coroutines-android where dispatch was happening using android.os.Handler.
For RoboVM there were several options:

To implement dispatch following was items should be possible in implementation:

  • cancellation of pending execution;
  • ability to determine if code currently executing inside context;

These are possible with Grand Dispatch Central and Operation Queues. Last are high level wrapper around GRC.

Implementation

My implementation is based on GDC as it is low level and introduces minimal amount of overhead.
While porting of Android part is straightforward cancellation and context identification API was missing in CocoaTouch. Following API were added as part of iOS14.5 bindings:

  • dispatch_block_* for scheduled block cancellation;
  • dispatch_queue_(get|set)_specific, dispatch_get_current_queue to allow tagging and identification of dispatch_queue as context.

Dispatch with cancellation implemented as bellow:

    override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle {
        val dispatchBlock = DispatchBlock.create(DispatchBlockFlags.None, block)
        dispatchQueue.after(timeMillis, TimeUnit.MILLISECONDS, dispatchBlock)
        return object : DisposableHandle {
            override fun dispose() {
                DispatchBlock.cancel(dispatchBlock)
            }
        }
    }

And identification:

    override fun isDispatchNeeded(context: CoroutineContext): Boolean {
        return !invokeImmediately || DispatchQueue.getCurrentSpecific(DispatchQueueAssociatedValues.key) != 
          dispatchQueue.getSpecific(DispatchQueueAssociatedValues.key)
    }

Source code

Source code is available in dkimitsa/kotlinx.coroutines.robovm.
Also it was built and deployed to sonatype maven repository and ready for use as dependency:

repositories {
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_version"
    implementation "io.github.dkimitsa.robovm:kotlinx-coroutines-robovm:0.1-SNAPSHOT"
}

Things to note

At moment of writing following pull request are not merged and required to be pulled in. And local version of RoboVM to be built from source code.

Comments