Skip to content

Commit cdfe07c

Browse files
committed
Clarify that using runBlocking in suspend functions is allowed
A user raised a concern that the internal coroutines machinery may break if `runBlocking` is used somewhere deeply in the call stack. Calling `runBlocking` from a `suspend` functions can lead to deadlocks naturally due to blocking the thread, or to surprising event ordering, but nothing is expected to break. This commit clarifies the exact danger of calling `runBlocking` from `suspend` functions.
1 parent 761bdeb commit cdfe07c

File tree

3 files changed

+49
-7
lines changed

3 files changed

+49
-7
lines changed

kotlinx-coroutines-core/concurrent/src/Builders.concurrent.kt

+17-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,21 @@ import kotlin.coroutines.*
44

55
/**
66
* Runs a new coroutine and **blocks** the current thread until its completion.
7-
* This function should not be used from a coroutine. It is designed to bridge regular blocking code
8-
* to libraries that are written in suspending style, to be used in `main` functions and in tests.
7+
*
8+
* It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in
9+
* `main` functions and in tests.
10+
*
11+
* Calling [runBlocking] from a suspend function is redundant.
12+
* For example, the following code is incorrect:
13+
* ```
14+
* suspend fun loadConfiguration() {
15+
* // DO NOT DO THIS:
16+
* val data = runBlocking { // <- redundant and blocks the thread, do not do that
17+
* fetchConfigurationData() // suspending function
18+
* }
19+
* ```
20+
*
21+
* Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will
22+
* block, potentially leading to thread starvation issues.
923
*/
10-
public expect fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T
24+
public expect fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T

kotlinx-coroutines-core/jvm/src/Builders.kt

+16-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,22 @@ import kotlin.coroutines.*
1010

1111
/**
1212
* Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
13-
* This function should not be used from a coroutine. It is designed to bridge regular blocking code
14-
* to libraries that are written in suspending style, to be used in `main` functions and in tests.
13+
*
14+
* It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in
15+
* `main` functions and in tests.
16+
*
17+
* Calling [runBlocking] from a suspend function is redundant.
18+
* For example, the following code is incorrect:
19+
* ```
20+
* suspend fun loadConfiguration() {
21+
* // DO NOT DO THIS:
22+
* val data = runBlocking { // <- redundant and blocks the thread, do not do that
23+
* fetchConfigurationData() // suspending function
24+
* }
25+
* ```
26+
*
27+
* Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will
28+
* block, potentially leading to thread starvation issues.
1529
*
1630
* The default [CoroutineDispatcher] for this builder is an internal implementation of event loop that processes continuations
1731
* in this blocked thread until the completion of this coroutine.

kotlinx-coroutines-core/native/src/Builders.kt

+16-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,22 @@ import kotlin.native.concurrent.*
88

99
/**
1010
* Runs new coroutine and **blocks** current thread _interruptibly_ until its completion.
11-
* This function should not be used from coroutine. It is designed to bridge regular blocking code
12-
* to libraries that are written in suspending style, to be used in `main` functions and in tests.
11+
*
12+
* It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in
13+
* `main` functions and in tests.
14+
*
15+
* Calling [runBlocking] from a suspend function is redundant.
16+
* For example, the following code is incorrect:
17+
* ```
18+
* suspend fun loadConfiguration() {
19+
* // DO NOT DO THIS:
20+
* val data = runBlocking { // <- redundant and blocks the thread, do not do that
21+
* fetchConfigurationData() // suspending function
22+
* }
23+
* ```
24+
*
25+
* Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will
26+
* block, potentially leading to thread starvation issues.
1327
*
1428
* The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations
1529
* in this blocked thread until the completion of this coroutine.

0 commit comments

Comments
 (0)