Skip to content

Commit 6a42a77

Browse files
elizarovqwwdfsadkoshachy
authored
Mark GlobalScope as delicate API and rewrite coroutine basics doc without it (#2637)
Also, remove the tutorial variant of the basic coroutine introduction as it essentially duplicates the content of the basic coroutine introduction, albeit with "how to create a project" additional information, which does not seem to be appropriate in this section at all. Fixes #2122 Co-authored-by: Vsevolod Tolstopyatov <[email protected]> Co-authored-by: Andrey Polyakov <[email protected]>
1 parent 8bb5210 commit 6a42a77

28 files changed

+347
-656
lines changed

docs/images/new-mvn-project-jvm.png

-370 KB
Binary file not shown.

docs/topics/composing-suspending-functions.md

+16-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ suspend fun doSomethingUsefulTwo(): Int {
2424

2525
What do we do if we need them to be invoked _sequentially_ &mdash; first `doSomethingUsefulOne` _and then_
2626
`doSomethingUsefulTwo`, and compute the sum of their results?
27-
In practice we do this if we use the result of the first function to make a decision on whether we need
27+
In practice, we do this if we use the result of the first function to make a decision on whether we need
2828
to invoke the second one or to decide on how to invoke it.
2929

3030
We use a normal sequential invocation, because the code in the coroutine, just like in the regular
@@ -190,18 +190,26 @@ standard `lazy` function in cases when computation of the value involves suspend
190190
## Async-style functions
191191

192192
We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo`
193-
_asynchronously_ using the [async] coroutine builder with an explicit [GlobalScope] reference.
193+
_asynchronously_ using the [async] coroutine builder using a [GlobalScope] reference to
194+
opt-out of the structured concurrency.
194195
We name such functions with the
195196
"...Async" suffix to highlight the fact that they only start asynchronous computation and one needs
196197
to use the resulting deferred value to get the result.
197198

199+
> [GlobalScope] is a delicate API that can backfire in non-trivial ways, one of which will be explained
200+
> below, so you must explicitly opt-in into using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`.
201+
>
202+
{type="note"}
203+
198204
```kotlin
199205
// The result type of somethingUsefulOneAsync is Deferred<Int>
206+
@OptIn(DelicateCoroutinesApi::class)
200207
fun somethingUsefulOneAsync() = GlobalScope.async {
201208
doSomethingUsefulOne()
202209
}
203210

204211
// The result type of somethingUsefulTwoAsync is Deferred<Int>
212+
@OptIn(DelicateCoroutinesApi::class)
205213
fun somethingUsefulTwoAsync() = GlobalScope.async {
206214
doSomethingUsefulTwo()
207215
}
@@ -236,10 +244,12 @@ fun main() {
236244
}
237245
//sampleEnd
238246

247+
@OptIn(DelicateCoroutinesApi::class)
239248
fun somethingUsefulOneAsync() = GlobalScope.async {
240249
doSomethingUsefulOne()
241250
}
242251

252+
@OptIn(DelicateCoroutinesApi::class)
243253
fun somethingUsefulTwoAsync() = GlobalScope.async {
244254
doSomethingUsefulTwo()
245255
}
@@ -272,9 +282,9 @@ Completed in 1085 ms
272282
{type="note"}
273283

274284
Consider what happens if between the `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic
275-
error in the code and the program throws an exception and the operation that was being performed by the program aborts.
285+
error in the code, and the program throws an exception, and the operation that was being performed by the program aborts.
276286
Normally, a global error-handler could catch this exception, log and report the error for developers, but the program
277-
could otherwise continue doing other operations. But here we have `somethingUsefulOneAsync` still running in the background,
287+
could otherwise continue doing other operations. However, here we have `somethingUsefulOneAsync` still running in the background,
278288
even though the operation that initiated it was aborted. This problem does not happen with structured
279289
concurrency, as shown in the section below.
280290

@@ -293,7 +303,7 @@ suspend fun concurrentSum(): Int = coroutineScope {
293303
}
294304
```
295305

296-
This way, if something goes wrong inside the code of the `concurrentSum` function and it throws an exception,
306+
This way, if something goes wrong inside the code of the `concurrentSum` function, and it throws an exception,
297307
all the coroutines that were launched in its scope will be cancelled.
298308

299309
<!--- CLEAR -->
@@ -403,4 +413,4 @@ Computation failed with ArithmeticException
403413
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
404414
[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
405415

406-
<!--- END -->
416+
<!--- END -->

docs/topics/coroutine-context-and-dispatchers.md

+15-11
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@ context of the main `runBlocking` coroutine which runs in the `main` thread.
6565
[Dispatchers.Unconfined] is a special dispatcher that also appears to run in the `main` thread, but it is,
6666
in fact, a different mechanism that is explained later.
6767

68-
The default dispatcher that is used when coroutines are launched in [GlobalScope]
69-
is represented by [Dispatchers.Default] and uses a shared background pool of threads,
70-
so `launch(Dispatchers.Default) { ... }` uses the same dispatcher as `GlobalScope.launch { ... }`.
68+
The default dispatcher that is used when no other dispatcher is explicitly specified in the scope.
69+
It is represented by [Dispatchers.Default] and uses a shared background pool of threads.
7170

7271
[newSingleThreadContext] creates a thread for the coroutine to run.
7372
A dedicated thread is a very expensive resource.
@@ -303,8 +302,14 @@ the [Job] of the new coroutine becomes
303302
a _child_ of the parent coroutine's job. When the parent coroutine is cancelled, all its children
304303
are recursively cancelled, too.
305304

306-
However, when [GlobalScope] is used to launch a coroutine, there is no parent for the job of the new coroutine.
307-
It is therefore not tied to the scope it was launched from and operates independently.
305+
However, this parent-child relation can be explicitly overriden in one of two ways:
306+
307+
1. When a different scope is explicitly specified when launching a coroutine (for example, `GlobalScope.launch`),
308+
then it does not inherit a `Job` from the parent scope.
309+
2. When a different `Job` object is passed as the context for the new coroutine (as show in the example below),
310+
then it overrides the `Job` of the parent scope.
311+
312+
In both cases, the launched coroutine is not tied to the scope it was launched from and operates independently.
308313

309314
```kotlin
310315
import kotlinx.coroutines.*
@@ -313,9 +318,9 @@ fun main() = runBlocking<Unit> {
313318
//sampleStart
314319
// launch a coroutine to process some kind of incoming request
315320
val request = launch {
316-
// it spawns two other jobs, one with GlobalScope
317-
GlobalScope.launch {
318-
println("job1: I run in GlobalScope and execute independently!")
321+
// it spawns two other jobs
322+
launch(Job()) {
323+
println("job1: I run in my own Job and execute independently!")
319324
delay(1000)
320325
println("job1: I am not affected by cancellation of the request")
321326
}
@@ -343,7 +348,7 @@ fun main() = runBlocking<Unit> {
343348
The output of this code is:
344349

345350
```text
346-
job1: I run in GlobalScope and execute independently!
351+
job1: I run in my own Job and execute independently!
347352
job2: I am a child of the request coroutine
348353
job1: I am not affected by cancellation of the request
349354
main: Who has survived request cancellation?
@@ -659,7 +664,6 @@ that should be implemented.
659664
[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
660665
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
661666
[Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html
662-
[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
663667
[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
664668
[newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-single-thread-context.html
665669
[ExecutorCoroutineDispatcher.close]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-executor-coroutine-dispatcher/close.html
@@ -678,4 +682,4 @@ that should be implemented.
678682
[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/ensure-present.html
679683
[ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html
680684

681-
<!--- END -->
685+
<!--- END -->

0 commit comments

Comments
 (0)