Skip to content

Richargh/coroutines-tornadofx-krdl-kt-sandbox

Repository files navigation

Coroutines Sandbox with TornadoFx

Features

  • Kotlin Coroutines

  • TornadoFx (see TornadoFx Guide)

  • How coroutines can make your code non-blocking

Usage

  • Build ./gradlew build

If you are on Java11 you need to add the OpenJfx dependencies because JavaFx was removed from the JDK. The following additional gradle dependencies might do the trick:

implementation("org.openjfx:javafx:11.0.2") implementation("org.openjfx:javafx-base:11.0.2") implementation("org.openjfx:javafx-controls:11.0.2")

More Examples

TornadoFx

Coroutines

  • Concurrency Sandbox especially contains many more examples, take a look at the tests if you want

Coroutines

Threads are used for preemptive multitasking. After a fixed time the OS switches from one thread to the next. Coroutines on the other hand are used for cooperative multitasking on the same thread. The programmer has to write code that yields to another coroutine and does not block the thread forever.

Kotlin Coroutines are assigned to threads by a Dispatcher. That means a coroutine that yields might run on a different thread when being resumed.

Coroutines in general are becoming very popular because they allow very efficient use of the available resources. NodeJs for example did only have a single thread for a long time and was still able to provide web server response times that matched or exceeded one-thread-per-request web servers from other programming languages.

The reason for this is that for each request the web server does very little computing and mostly waits for other I/O-operations to terminate. To fulfill the request GET localhost:8080/books?named=Foo the web server usually needs to access some kind of external database. Many web servers block the thread until the database returns the list of books.

Node can use the event loop, process other web requests, and will be notified once the database operation completes. A Coroutine-based server similarly can suspend the coroutine until the database operation completes. Until the operation completes another coroutine can use the thread to answer another request.

Coroutines

Structured Concurrency

Kotlin Coroutines follow a principle the authors named "structured concurrency": a coroutine scope only terminates when all it’s children coroutine scopes have also terminated, and all children inherit the context from it’s parent.

That means we can write concurrent code that looks sequential and we can never forget to call .await() or similar operations.

Implementation Details

The Kotlin compiler uses a concept called continuation under the hood to implement suspending functions. Let’s look at an example:

First we’ll create a regular class:

  1. First create a file Sample.kt and add only the following line fun aMethod(aParam: Int): Int{ return 42 }

  2. Compile it with the Kotlin compiler. In my case it’s the one IntelliJ uses: /Applications/IntelliJ\ IDEA.app/Contents/plugins/Kotlin/kotlinc/bin/kotlinc Sample.kt

  3. Decompile it to find a regular Java class: javap SampleKt.class:

public final class SampleKt {
  public static final int aMethod(int);
}

Next we can compare this with suspending code:

  1. Modify Sample.kt so that the line now contains suspend suspend fun aMethod(aParam: Int): Int{ return 42 }

  2. Compile it again

  3. After decompilation it now reads:

public final class SampleKt {
  public static final java.lang.Object aMethod(int, kotlin.coroutines.Continuation<? super java.lang.Integer>); // (1)
}
  1. the method now returns an Object and takes an additional Continuation<Int>

The kotlin.coroutines.Continuation<Int> interface has the method fun resumeWith(result: Result<Int>). This is used to resume the execution of the corresponding coroutine. We pass a successful or failed [result] as the return value of the last suspension point.

Coroutine Cheatsheet

Coroutine Cheatsheet

Enter Coroutine Scope

To go from the blocking world into coroutine land we either:

  • use runBlocking { /* coroutine code here */}

  • delegating to a CoroutineScope class Myclass(context: CoroutineContext): CoroutineScope by CoroutineScope(context)

There are two more options which might fit your use case, they do have a major problem though. We can:

  • implement CoroutineScope interface (which I use for CenterView)

  • or use GlobalScope.launch {}

The major feature of Kotlin Coroutines is structured concurrency, but the last two options don’t inherit the scope from their parent. They do propagate their scope to their children though, so use them with caution.

Dispatcher

  • Main dispatcher, usually single-thread, useful for UI operations: Dispatcher.Main

  • Default dispatcher, threadcount usually equal to CPU-core count, useful for CPU-intensive computation: Dispatcher.Default

  • IO dispatcher, threadcount usually 64, useful for operations that wait for IO to complete: Dispatcher.IO

  • Single Thread context, always single-threaded, maybe useful for you: newSingleThreadContext("name")

Coroutine Builder

  • Async op where you care for the result and await() completion: async {}

  • Execute op in a new context and suspend until result: withContext(Dispatcher.IO) {}

  • Fire and forget async op: launch {}

  • Enter coroutine land and block thread until completion: runBlocking {}

About

Coroutines with Tornadofx (Sandbox)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages