SuspendApp: A New Arrow Library

SuspendApp: A New Arrow Library

There is a new library in the Arrow ecosystem, and its only base dependency is KotlinX Coroutines.

SuspendApp offers an application builder that gives strong guarantees in terms of application termination. It enables you to reason about Resource safety in terms of Structured Concurrency, which allows us to safely compose suspend fun. When building applications with such requirements, it typically requires us to write a lot of platform-specific code. This library aims to solve that problem by leveraging Kotlin MPP.

Setting up SuspendApp with Gradle is quite easy. Simply add the following coordinates to your dependencies block, io.arrow-kt:suspendapp:0.3.0. It’ll automatically pull in the correct dependency depending on the configured target platforms. Full documentation can be found on the project website.

An example SuspendApp

Once the dependency is in, bringing the safety guarantees is as simple as wrapping the entire main with SuspendApp. Let’s discuss with a concrete example, which are those guarantees:

fun main() = SuspendApp {
  try {
    println("App Started!  Waiting until asked to shutdown.")
    while (true) {
      delay(2_500)
      println("Ping")
    }
  } catch (e: CancellationException) {
    println("Cleaning up App... will take 10 seconds...")
    withContext(NonCancellable) { delay(10_000) }
    println("Done cleaning up. Will release app to exit")
  }
}

When you run this example, it will:

  1. print “App Started! Waiting until asked to shutdown.”
  2. Loop until SuspendApp gets cancelled, and it’ll print “Ping” every 2,5 seconds.
  3. Upon cancellation it will:
    1. print “Cleaning up App… will take 10 seconds…”
    2. Delay for 10 seconds
    3. print “Done cleaning up. Will release app to exit”

To build and run this example, we should just build the binaries and execute them. For example for MacOs and NodeJS.

./gradlew build
build/bin/macosX64/releaseExecutable/example.kexe
node build/compileSync/main/productionExecutable/kotlin/suspendapp-example.js

After running the binary, we’ll see the following getting printed:

App Started!  Waiting until asked to shutdown.
Ping
Ping

When we press CTRL+c (SIGINT), we’ll see the application shutting down.

^CCleaning up App... will take 10 seconds...
Done cleaning up. Will release app to exit

In this example, the cleaning has been simulated by a 10 second delay. This is not an unrealistic scenario; for example, in the docs, we cover the case of a Ktor application that must support graceful shutdown when managed by Kubernetes.

SuspendApp vs runBlocking

KotlinX Coroutines offers us runBlocking out-of-the-box for native and JVM platforms, but not for js. SuspendApp is available for native, JVM, and js (node), so it allows you to define an application within commonMain for these targets.

Additionally, if we replace SuspendApp with runBlocking in the above example, the clean-up steps never execute.

App Started!  Waiting until asked to shutdown.
Ping
Ping
^C

The clean-up steps never execute because runBlocking is not taking into account termination events from the platforms. runBlocking doesn’t wait for cancellation or resource clean-up and simply shuts down the process upon all termination events. Instead, SuspendApp builds on top of runBlocking and observes termination events from the platform awaiting cancellation, resource clean-up, and following the rules of structured concurrency.

Conclusion

Reason about resource safety in the same way you reason about Structured Concurrency with SuspendApp! SuspendApp gives strong guarantees about resource-safety, and structured concurrency supporting a Kotlin Multiplatform commonMain application entry-point.

If you have any feedback or suggestions, please share them in the #arrow channel on KotlinLang Slack or the Github Repository.

Ensure the success of your project

47 Degrees can work with you to help manage the risks of technology evolution, develop a team of top-tier engaged developers, improve productivity, lower maintenance cost, increase hardware utilization, and improve product quality; all while using the best technologies.