@@ -33,19 +33,65 @@ public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobIm
3333public fun SupervisorJob0 (parent : Job ? = null) : Job = SupervisorJob (parent)
3434
3535/* *
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.
4038 *
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 .
4341 *
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.
4647 *
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.
4995 */
5096public suspend fun <R > supervisorScope (block : suspend CoroutineScope .() -> R ): R {
5197 contract {
0 commit comments