|
1 | 1 | @file:JvmMultifileClass
|
2 | 2 | @file:JvmName("BuildersKt")
|
3 |
| -@file:OptIn(ExperimentalContracts::class) |
4 |
| -@file:Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND") |
5 | 3 |
|
6 | 4 | package kotlinx.coroutines
|
7 | 5 |
|
8 |
| -import java.util.concurrent.locks.* |
9 |
| -import kotlin.contracts.* |
10 | 6 | import kotlin.coroutines.*
|
11 | 7 |
|
12 | 8 | /**
|
13 |
| - * Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion. |
| 9 | + * The same as [runBlocking], but for consumption from Java. |
| 10 | + * From Kotlin's point of view, this function has the exact same signature as the regular [runBlocking]. |
| 11 | + * This is done so that it can not be called from Kotlin, despite the fact that it is public. |
14 | 12 | *
|
15 |
| - * It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in |
16 |
| - * `main` functions and in tests. |
| 13 | + * We do not expose this [runBlocking] in the documentation, because it is not supposed to be used from Kotlin. |
17 | 14 | *
|
18 |
| - * Calling [runBlocking] from a suspend function is redundant. |
19 |
| - * For example, the following code is incorrect: |
20 |
| - * ``` |
21 |
| - * suspend fun loadConfiguration() { |
22 |
| - * // DO NOT DO THIS: |
23 |
| - * val data = runBlocking { // <- redundant and blocks the thread, do not do that |
24 |
| - * fetchConfigurationData() // suspending function |
25 |
| - * } |
26 |
| - * ``` |
27 |
| - * |
28 |
| - * Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will |
29 |
| - * block, potentially leading to thread starvation issues. |
30 |
| - * |
31 |
| - * The default [CoroutineDispatcher] for this builder is an internal implementation of event loop that processes continuations |
32 |
| - * in this blocked thread until the completion of this coroutine. |
33 |
| - * See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`. |
34 |
| - * |
35 |
| - * When [CoroutineDispatcher] is explicitly specified in the [context], then the new coroutine runs in the context of |
36 |
| - * the specified dispatcher while the current thread is blocked. If the specified dispatcher is an event loop of another `runBlocking`, |
37 |
| - * then this invocation uses the outer event loop. |
38 |
| - * |
39 |
| - * If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and |
40 |
| - * this `runBlocking` invocation throws [InterruptedException]. |
41 |
| - * |
42 |
| - * See [newCoroutineContext][CoroutineScope.newCoroutineContext] for a description of debugging facilities that are available |
43 |
| - * for a newly created coroutine. |
44 |
| - * |
45 |
| - * @param context the context of the coroutine. The default value is an event loop on the current thread. |
46 |
| - * @param block the coroutine code. |
| 15 | + * @suppress |
47 | 16 | */
|
48 | 17 | @Throws(InterruptedException::class)
|
49 |
| -public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { |
50 |
| - contract { |
51 |
| - callsInPlace(block, InvocationKind.EXACTLY_ONCE) |
52 |
| - } |
53 |
| - val currentThread = Thread.currentThread() |
54 |
| - val contextInterceptor = context[ContinuationInterceptor] |
55 |
| - val eventLoop: EventLoop? |
56 |
| - val newContext: CoroutineContext |
57 |
| - if (contextInterceptor == null) { |
58 |
| - // create or use private event loop if no dispatcher is specified |
59 |
| - eventLoop = ThreadLocalEventLoop.eventLoop |
60 |
| - newContext = GlobalScope.newCoroutineContext(context + eventLoop) |
61 |
| - } else { |
62 |
| - // See if context's interceptor is an event loop that we shall use (to support TestContext) |
63 |
| - // or take an existing thread-local event loop if present to avoid blocking it (but don't create one) |
64 |
| - eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() } |
65 |
| - ?: ThreadLocalEventLoop.currentOrNull() |
66 |
| - newContext = GlobalScope.newCoroutineContext(context) |
67 |
| - } |
68 |
| - val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop) |
| 18 | +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") |
| 19 | +@kotlin.internal.LowPriorityInOverloadResolution |
| 20 | +public fun <T> runBlocking( |
| 21 | + context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T |
| 22 | +): T = runBlocking(context, block) |
| 23 | + |
| 24 | +@Throws(InterruptedException::class) |
| 25 | +internal actual fun <T> runBlockingImpl( |
| 26 | + newContext: CoroutineContext, eventLoop: EventLoop?, block: suspend CoroutineScope.() -> T |
| 27 | +): T { |
| 28 | + val coroutine = BlockingCoroutine<T>(newContext, Thread.currentThread(), eventLoop) |
69 | 29 | coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
|
70 | 30 | return coroutine.joinBlocking()
|
71 | 31 | }
|
|
0 commit comments