blob: 8436991223c3e4d66635c18fe1c3209b03d588c4 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03002 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Roman Elizarovf16fd272017-02-07 11:26:00 +03003 */
4
Roman Elizarovaa461cf2018-04-11 13:20:29 +03005@file:JvmMultifileClass
6@file:JvmName("BuildersKt")
Roman Elizarovf161c9f2018-02-07 23:27:10 +03007
Roman Elizarov3754f952017-01-18 20:47:54 +03008package kotlinx.coroutines.experimental
9
Roman Elizarov2adf8bc2018-01-24 20:09:57 +030010import java.util.concurrent.locks.*
Roman Elizarovea4a51b2017-01-31 12:01:25 +030011import kotlin.coroutines.experimental.*
Roman Elizarov3754f952017-01-18 20:47:54 +030012
13/**
Roman Elizarov32d95322017-02-09 15:57:31 +030014 * Runs new coroutine and **blocks** current thread _interruptibly_ until its completion.
Roman Elizarovd528e3e2017-01-23 15:40:05 +030015 * This function should not be used from coroutine. It is designed to bridge regular blocking code
16 * to libraries that are written in suspending style, to be used in `main` functions and in tests.
17 *
18 * The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations
19 * in this blocked thread until the completion of this coroutine.
20 * See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`.
Roman Elizarov3754f952017-01-18 20:47:54 +030021 *
Roman Elizarovcb787872018-01-29 18:07:21 +030022 * When [CoroutineDispatcher] is explicitly specified in the [context], then the new coroutine runs in the context of
23 * the specified dispatcher while the current thread is blocked. If the specified dispatcher implements [EventLoop]
24 * interface and this `runBlocking` invocation is performed from inside of the this event loop's thread, then
25 * this event loop is processed using its [processNextEvent][EventLoop.processNextEvent] method until coroutine completes.
26 *
Roman Elizarov3754f952017-01-18 20:47:54 +030027 * If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
28 * this `runBlocking` invocation throws [InterruptedException].
29 *
30 * See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
Roman Elizarovc0d559b2017-09-28 14:27:05 +030031 *
32 * @param context context of the coroutine. The default value is an implementation of [EventLoop].
33 * @param block the coroutine code.
Roman Elizarov3754f952017-01-18 20:47:54 +030034 */
35@Throws(InterruptedException::class)
Roman Elizarovbddb1d72017-12-25 17:16:08 +030036public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
Roman Elizarovd528e3e2017-01-23 15:40:05 +030037 val currentThread = Thread.currentThread()
Roman Elizarovcb787872018-01-29 18:07:21 +030038 val contextInterceptor = context[ContinuationInterceptor]
39 val privateEventLoop = contextInterceptor == null // create private event loop if no dispatcher is specified
40 val eventLoop = if (privateEventLoop) BlockingEventLoop(currentThread) else contextInterceptor as? EventLoop
41 val newContext = newCoroutineContext(
42 if (privateEventLoop) context + (eventLoop as ContinuationInterceptor) else context
43 )
44 val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop, privateEventLoop)
Roman Elizarov2adf8bc2018-01-24 20:09:57 +030045 coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
Roman Elizarovd528e3e2017-01-23 15:40:05 +030046 return coroutine.joinBlocking()
47}
Roman Elizarov3754f952017-01-18 20:47:54 +030048
Roman Elizarovd528e3e2017-01-23 15:40:05 +030049private class BlockingCoroutine<T>(
Roman Elizarov2b12d582017-06-22 20:12:19 +030050 parentContext: CoroutineContext,
Roman Elizarov4518e052017-03-02 22:48:27 +030051 private val blockedThread: Thread,
Roman Elizarovcb787872018-01-29 18:07:21 +030052 private val eventLoop: EventLoop?,
Roman Elizarov4518e052017-03-02 22:48:27 +030053 private val privateEventLoop: Boolean
Roman Elizarov2b12d582017-06-22 20:12:19 +030054) : AbstractCoroutine<T>(parentContext, true) {
Roman Elizarov4518e052017-03-02 22:48:27 +030055 init {
Roman Elizarov8b38fa22017-09-27 17:44:31 +030056 if (privateEventLoop) require(eventLoop is BlockingEventLoop)
Roman Elizarov4518e052017-03-02 22:48:27 +030057 }
58
Roman Elizarovebc88662018-01-24 23:58:56 +030059 override fun onCancellationInternal(exceptionally: CompletedExceptionally?) {
Roman Elizarov8b38fa22017-09-27 17:44:31 +030060 // wake up blocked thread
Roman Elizarova4becc92017-01-24 11:45:50 +030061 if (Thread.currentThread() != blockedThread)
62 LockSupport.unpark(blockedThread)
Roman Elizarov3754f952017-01-18 20:47:54 +030063 }
64
65 @Suppress("UNCHECKED_CAST")
66 fun joinBlocking(): T {
Roman Elizarov35d2c342017-07-20 14:54:39 +030067 timeSource.registerTimeLoopThread()
Roman Elizarov4518e052017-03-02 22:48:27 +030068 while (true) {
Roman Elizarov3754f952017-01-18 20:47:54 +030069 if (Thread.interrupted()) throw InterruptedException().also { cancel(it) }
Roman Elizarov4518e052017-03-02 22:48:27 +030070 val parkNanos = eventLoop?.processNextEvent() ?: Long.MAX_VALUE
Roman Elizarov35d2c342017-07-20 14:54:39 +030071 // note: process next even may loose unpark flag, so check if completed before parking
Roman Elizarov7400ff02017-07-10 17:56:41 +030072 if (isCompleted) break
Roman Elizarov35d2c342017-07-20 14:54:39 +030073 timeSource.parkNanos(this, parkNanos)
Roman Elizarov3754f952017-01-18 20:47:54 +030074 }
Roman Elizarov4518e052017-03-02 22:48:27 +030075 // process queued events (that could have been added after last processNextEvent and before cancel
Roman Elizarov0d35c852017-10-07 23:19:22 +030076 if (privateEventLoop) (eventLoop as BlockingEventLoop).apply {
77 // We exit the "while" loop above when this coroutine's state "isCompleted",
78 // Here we should signal that BlockingEventLoop should not accept any more tasks
79 isCompleted = true
80 shutdown()
81 }
Roman Elizarov35d2c342017-07-20 14:54:39 +030082 timeSource.unregisterTimeLoopThread()
Roman Elizarovd528e3e2017-01-23 15:40:05 +030083 // now return result
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030084 val state = this.state
Vsevolod Tolstopyatovc1092d52018-04-12 20:22:25 +030085 (state as? CompletedExceptionally)?.let { throw it.cause }
Roman Elizarov3754f952017-01-18 20:47:54 +030086 return state as T
87 }
Roman Elizarov3754f952017-01-18 20:47:54 +030088}