@@ -33,19 +33,65 @@ public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobIm
33
33
public fun SupervisorJob0 (parent : Job ? = null) : Job = SupervisorJob (parent)
34
34
35
35
/* *
36
- * Creates a [CoroutineScope] with [SupervisorJob] and calls the specified suspend [block] with this scope.
37
- * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, using the
38
- * [Job] from that context as the parent for the new [SupervisorJob].
39
- * This function returns as soon as the given block and all its child coroutines are completed.
36
+ * Runs the given [block] in-place in a new [CoroutineScope] with a [SupervisorJob]
37
+ * based on the caller coroutine context, returning its result.
40
38
*
41
- * Unlike [coroutineScope], a failure of a child does not cause this scope to fail and does not affect its other children,
42
- * so a custom policy for handling failures of its children can be implemented. See [SupervisorJob] for additional details .
39
+ * The lifecycle of the new [SupervisorJob] begins with starting the [block] and completes when both the [block] and
40
+ * all the coroutines launched in the scope complete .
43
41
*
44
- * If an exception happened in [block], then the supervisor job is failed and all its children are cancelled.
45
- * If the current coroutine was cancelled, then both the supervisor job itself and all its children are cancelled.
42
+ * The context of the new scope is obtained by combining the [currentCoroutineContext] with a new [SupervisorJob]
43
+ * whose parent is the [Job] of the caller [currentCoroutineContext] (if any).
44
+ * The [SupervisorJob] of the new scope is not a normal child of the caller coroutine but a lexically scoped one,
45
+ * meaning that the failure of the [SupervisorJob] will not affect the parent [Job].
46
+ * Instead, the exception leading to the failure will be rethrown to the caller of this function.
46
47
*
47
- * The method may throw a [CancellationException] if the current job was cancelled externally,
48
- * or rethrow an exception thrown by the given [block].
48
+ * If a child coroutine launched in the new scope fails, it will not affect the other children of the scope.
49
+ * However, if the [block] finishes with an exception, it will cancel the scope and all its children.
50
+ * See [coroutineScope] for a similar function that treats every child coroutine as crucial for obtaining the result
51
+ * and cancels the whole computation if one of them fails.
52
+ *
53
+ * Together, this makes [supervisorScope] a good choice for launching multiple coroutines where some failures
54
+ * are acceptable and should not affect the others.
55
+ *
56
+ * ```
57
+ * // cancelling the caller's coroutine will cancel the new scope and all its children
58
+ * suspend fun tryDownloadFiles(urls: List<String>): List<Deferred<ByteArray>> =
59
+ * supervisorScope {
60
+ * urls.map { url ->
61
+ * async {
62
+ * // if one of the downloads fails, the others will continue
63
+ * donwloadFileContent(url)
64
+ * }
65
+ * }
66
+ * } // every download will fail or complete by the time this function returns
67
+ * ```
68
+ *
69
+ * Rephrasing this in more practical terms, the specific list of structured concurrency interactions is as follows:
70
+ * - Cancelling the caller's [currentCoroutineContext] leads to cancellation of the new [CoroutineScope]
71
+ * (corresponding to the code running in the [block]), which in turn cancels all the coroutines launched in it.
72
+ * - If the [block] fails with an exception, the exception is rethrown to the caller,
73
+ * without directly affecting the caller's [Job].
74
+ * - [supervisorScope] will only finish when all the coroutines launched in it finish.
75
+ * After that, the [supervisorScope] returns (or rethrows) the result of the [block] to the caller.
76
+ *
77
+ * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled
78
+ * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
79
+ *
80
+ * **Pitfall**: [supervisorScope] does not install a [CoroutineExceptionHandler] in the new scope.
81
+ * This means that if a child coroutine started with [launch] fails, its exception will be unhandled,
82
+ * possibly crashing the program. Use the following pattern to avoid this:
83
+ *
84
+ * ```
85
+ * withContext(CoroutineExceptionHandler { _, exception ->
86
+ * // handle the exceptions as needed
87
+ * }) {
88
+ * supervisorScope {
89
+ * // launch child coroutines here
90
+ * }
91
+ * }
92
+ * ```
93
+ *
94
+ * Alternatively, the [CoroutineExceptionHandler] can be supplied to the newly launched coroutines themselves.
49
95
*/
50
96
public suspend fun <R > supervisorScope (block : suspend CoroutineScope .() -> R ): R {
51
97
contract {
0 commit comments