private object ApplicationEventLoop : CoroutineDispatcher() { val thread = Application.getMainThread() lateinit var runnable: Runnable @Volatile var context: CoroutineContext = EmptyCoroutineContext @Volatile var former: CoroutineContext = context val marker = object : CoroutineContext.Element, CoroutineContext.Key { override val key = this } override fun dispatch(context: CoroutineContext, block: Runnable) { runnable = block former = SpigotEventLoop.context SpigotEventLoop.context = context LockSupport.unpark(thread) } class ApplicationCoroutine : AbstractCoroutine( context.minusKey(Job) + ApplicationEventLoop + marker, true, true ) { @Volatile var result: Type? = null @Volatile var reason: Throwable? = null override fun afterCompletion(state: Any?) = LockSupport.unpark(thread) override fun onCompleted(value: Type) { result = value } override fun onCancelled(cause: Throwable, handled: Boolean) { reason = cause } fun joinBlocking(): Type { try { do { LockSupport.park() runnable.run() ApplicationEventLoop.context = former } while (!isCompleted) } catch (reason: Exception) { cancelInternal(reason) } reason?.let { throw it } return result as Type } } } fun blocking(block: suspend CoroutineScope.() -> (Return)): Return { if (Thread.currentThread() != ApplicationEventLoop.thread) error("Must call blocking from main thread!") val coroutine = ApplicationEventLoop.ApplicationCoroutine() coroutine.start(CoroutineStart.UNDISPATCHED, coroutine, block) return coroutine.joinBlocking() } object ApplicationDispatcher : CoroutineDispatcher(), Delay { override fun dispatch(context: CoroutineContext, block: Runnable) { fun execute() { val saved = ApplicationEventLoop.context try { ApplicationEventLoop.context = context; block.run() } finally { ApplicationEventLoop.context = saved } } if (Thread.currentThread() === ApplicationEventLoop.thread) execute() else if (context[ApplicationEventLoop.maker] != null) ApplicationEventLoop.dispatch(context, block) else Application.getScheduler().runNextCycle(::execute) } override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { val task = Application.getScheduler().runInCycles(Runnable { continuation.resume(Unit) }, millisToCycles(timeMillis)) continuation.invokeOnCancellation { task.cancel() } } } val Dispatchers.Application get() = ApplicationDispatcher