Skip to content

Instantly share code, notes, and snippets.

@Groostav
Last active August 5, 2019 21:35
Show Gist options
  • Save Groostav/49cce5f623d96679abbccfeca295da4a to your computer and use it in GitHub Desktop.
Save Groostav/49cce5f623d96679abbccfeca295da4a to your computer and use it in GitHub Desktop.
Strategy to synchronously enter asynchronous code
package com.empowerops.common
import com.sun.javafx.tk.Toolkit
import kotlinx.coroutines.*
import kotlinx.coroutines.javafx.JavaFx
import java.util.*
private class ExceptionWrapper(val ex: Throwable)
private val toolkit = Toolkit.getToolkit()
fun <R> enterNestedJavaFxEventLoop(block: suspend CoroutineScope.() -> R): R {
require(BootstrappingUtilities.isFXApplicationThread())
//this is required to avoid a race in the enter/exit strategy.
val key = UUID.randomUUID()
// regarding races: as long as launch does not run eagerly,
// then we know for certainty that this job is in the queue blocked by this current running method
// thus we cannot exit the nested event loop before we enter it
GlobalScope.launch(Dispatchers.JavaFx, CoroutineStart.DEFAULT){
try {
val result = block()
toolkit.exitNestedEventLoop(key, result)
}
catch(ex: Throwable){
toolkit.exitNestedEventLoop(key, ExceptionWrapper(ex))
}
}
val result = toolkit.enterNestedEventLoop(key)
when {
//note: we dont need to wrap result.ex because of the nested event loop strategy.
// remember this is all call-stack games, we dont actually have any suspension here,
// so we dont need to mangle exceptions to make them readable.
// alternatively put: any exception generated here will have all of its stacktrace in result.ex's stacktrace.
result is ExceptionWrapper -> throw result.ex
else -> @Suppress("UNCHECKED_CAST") return result as R
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment