• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 @file:OptIn(ExperimentalContracts::class)
5 
6 package kotlinx.coroutines
7 
8 import kotlinx.coroutines.internal.*
9 import kotlinx.coroutines.intrinsics.*
10 import kotlin.contracts.*
11 import kotlin.coroutines.*
12 import kotlin.coroutines.intrinsics.*
13 
14 /**
15  * Defines a scope for new coroutines. Every **coroutine builder** (like [launch], [async], etc.)
16  * is an extension on [CoroutineScope] and inherits its [coroutineContext][CoroutineScope.coroutineContext]
17  * to automatically propagate all its elements and cancellation.
18  *
19  * The best ways to obtain a standalone instance of the scope are [CoroutineScope()] and [MainScope()] factory functions,
20  * taking care to cancel these coroutine scopes when they are no longer needed (see section on custom usage below for
21  * explanation and example).
22  *
23  * Additional context elements can be appended to the scope using the [plus][CoroutineScope.plus] operator.
24  *
25  * ### Convention for structured concurrency
26  *
27  * Manual implementation of this interface is not recommended, implementation by delegation should be preferred instead.
28  * By convention, the [context of a scope][CoroutineScope.coroutineContext] should contain an instance of a
29  * [job][Job] to enforce the discipline of **structured concurrency** with propagation of cancellation.
30  *
31  * Every coroutine builder (like [launch], [async], and others)
32  * and every scoping function (like [coroutineScope] and [withContext]) provides _its own_ scope
33  * with its own [Job] instance into the inner block of code it runs.
34  * By convention, they all wait for all the coroutines inside their block to complete before completing themselves,
35  * thus enforcing the structured concurrency. See [Job] documentation for more details.
36  *
37  * ### Android usage
38  *
39  * Android has first-party support for coroutine scope in all entities with the lifecycle.
40  * See [the corresponding documentation](https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope).
41  *
42  * ### Custom usage
43  *
44  * `CoroutineScope` should be declared as a property on entities with a well-defined lifecycle that are
45  * responsible for launching child coroutines. The corresponding instance of `CoroutineScope` shall be created
46  * with either `CoroutineScope()` or `MainScope()`:
47  *
48  * * `CoroutineScope()` uses the [context][CoroutineContext] provided to it as a parameter for its coroutines
49  *   and adds a [Job] if one is not provided as part of the context.
50  * * `MainScope()` uses [Dispatchers.Main] for its coroutines and has a [SupervisorJob].
51  *
52  * **The key part of custom usage of `CoroutineScope` is cancelling it at the end of the lifecycle.**
53  * The [CoroutineScope.cancel] extension function shall be used when the entity that was launching coroutines
54  * is no longer needed. It cancels all the coroutines that might still be running on behalf of it.
55  *
56  * For example:
57  *
58  * ```
59  * class MyUIClass {
60  *     val scope = MainScope() // the scope of MyUIClass, uses Dispatchers.Main
61  *
62  *     fun destroy() { // destroys an instance of MyUIClass
63  *         scope.cancel() // cancels all coroutines launched in this scope
64  *         // ... do the rest of cleanup here ...
65  *     }
66  *
67  *     /*
68  *      * Note: if this instance is destroyed or any of the launched coroutines
69  *      * in this method throws an exception, then all nested coroutines are cancelled.
70  *      */
71  *     fun showSomeData() = scope.launch { // launched in the main thread
72  *        // ... here we can use suspending functions or coroutine builders with other dispatchers
73  *        draw(data) // draw in the main thread
74  *     }
75  * }
76  * ```
77  */
78 public interface CoroutineScope {
79     /**
80      * The context of this scope.
81      * Context is encapsulated by the scope and used for implementation of coroutine builders that are extensions on the scope.
82      * Accessing this property in general code is not recommended for any purposes except accessing the [Job] instance for advanced usages.
83      *
84      * By convention, should contain an instance of a [job][Job] to enforce structured concurrency.
85      */
86     public val coroutineContext: CoroutineContext
87 }
88 
89 /**
90  * Adds the specified coroutine context to this scope, overriding existing elements in the current
91  * scope's context with the corresponding keys.
92  *
93  * This is a shorthand for `CoroutineScope(thisScope + context)`.
94  */
plusnull95 public operator fun CoroutineScope.plus(context: CoroutineContext): CoroutineScope =
96     ContextScope(coroutineContext + context)
97 
98 /**
99  * Creates the main [CoroutineScope] for UI components.
100  *
101  * Example of use:
102  * ```
103  * class MyAndroidActivity {
104  *     private val scope = MainScope()
105  *
106  *     override fun onDestroy() {
107  *         super.onDestroy()
108  *         scope.cancel()
109  *     }
110  * }
111  * ```
112  *
113  * The resulting scope has [SupervisorJob] and [Dispatchers.Main] context elements.
114  * If you want to append additional elements to the main scope, use [CoroutineScope.plus] operator:
115  * `val scope = MainScope() + CoroutineName("MyActivity")`.
116  */
117 @Suppress("FunctionName")
118 public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
119 
120 /**
121  * Returns `true` when the current [Job] is still active (has not completed and was not cancelled yet).
122  *
123  * Check this property in long-running computation loops to support cancellation:
124  * ```
125  * while (isActive) {
126  *     // do some computation
127  * }
128  * ```
129  *
130  * This property is a shortcut for `coroutineContext.isActive` in the scope when
131  * [CoroutineScope] is available.
132  * See [coroutineContext][kotlin.coroutines.coroutineContext],
133  * [isActive][kotlinx.coroutines.isActive] and [Job.isActive].
134  */
135 @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
136 public val CoroutineScope.isActive: Boolean
137     get() = coroutineContext[Job]?.isActive ?: true
138 
139 /**
140  * A global [CoroutineScope] not bound to any job.
141  * Global scope is used to launch top-level coroutines which are operating on the whole application lifetime
142  * and are not cancelled prematurely.
143  *
144  * Active coroutines launched in `GlobalScope` do not keep the process alive. They are like daemon threads.
145  *
146  * This is a **delicate** API. It is easy to accidentally create resource or memory leaks when
147  * `GlobalScope` is used. A coroutine launched in `GlobalScope` is not subject to the principle of structured
148  * concurrency, so if it hangs or gets delayed due to a problem (e.g. due to a slow network), it will stay working
149  * and consuming resources. For example, consider the following code:
150  *
151  * ```
152  * fun loadConfiguration() {
153  *     GlobalScope.launch {
154  *         val config = fetchConfigFromServer() // network request
155  *         updateConfiguration(config)
156  *     }
157  * }
158  * ```
159  *
160  * A call to `loadConfiguration` creates a coroutine in the `GlobalScope` that works in background without any
161  * provision to cancel it or to wait for its completion. If a network is slow, it keeps waiting in background,
162  * consuming resources. Repeated calls to `loadConfiguration` will consume more and more resources.
163  *
164  * ### Possible replacements
165  *
166  * In many cases uses of `GlobalScope` should be removed, marking the containing operation with `suspend`, for example:
167  *
168  * ```
169  * suspend fun loadConfiguration() {
170  *     val config = fetchConfigFromServer() // network request
171  *     updateConfiguration(config)
172  * }
173  * ```
174  *
175  * In cases when `GlobalScope.launch` was used to launch multiple concurrent operations, the corresponding
176  * operations shall be grouped with [coroutineScope] instead:
177  *
178  * ```
179  * // concurrently load configuration and data
180  * suspend fun loadConfigurationAndData() {
181  *     coroutineScope {
182  *         launch { loadConfiguration() }
183  *         launch { loadData() }
184  *     }
185  * }
186  * ```
187  *
188  * In top-level code, when launching a concurrent operation from a non-suspending context, an appropriately
189  * confined instance of [CoroutineScope] shall be used instead of a `GlobalScope`. See docs on [CoroutineScope] for
190  * details.
191  *
192  * ### GlobalScope vs custom scope
193  *
194  * Do not replace `GlobalScope.launch { ... }` with `CoroutineScope().launch { ... }` constructor function call.
195  * The latter has the same pitfalls as `GlobalScope`. See [CoroutineScope] documentation on the intended usage of
196  * `CoroutineScope()` constructor function.
197  *
198  * ### Legitimate use-cases
199  *
200  * There are limited circumstances under which `GlobalScope` can be legitimately and safely used, such as top-level background
201  * processes that must stay active for the whole duration of the application's lifetime. Because of that, any use
202  * of `GlobalScope` requires an explicit opt-in with `@OptIn(DelicateCoroutinesApi::class)`, like this:
203  *
204  * ```
205  * // A global coroutine to log statistics every second, must be always active
206  * @OptIn(DelicateCoroutinesApi::class)
207  * val globalScopeReporter = GlobalScope.launch {
208  *     while (true) {
209  *         delay(1000)
210  *         logStatistics()
211  *     }
212  * }
213  * ```
214  */
215 @DelicateCoroutinesApi
216 public object GlobalScope : CoroutineScope {
217     /**
218      * Returns [EmptyCoroutineContext].
219      */
220     override val coroutineContext: CoroutineContext
221         get() = EmptyCoroutineContext
222 }
223 
224 /**
225  * Creates a [CoroutineScope] and calls the specified suspend block with this scope.
226  * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides
227  * the context's [Job].
228  *
229  * This function is designed for _parallel decomposition_ of work. When any child coroutine in this scope fails,
230  * this scope fails and all the rest of the children are cancelled (for a different behavior see [supervisorScope]).
231  * This function returns as soon as the given block and all its children coroutines are completed.
232  * A usage example of a scope looks like this:
233  *
234  * ```
235  * suspend fun showSomeData() = coroutineScope {
236  *     val data = async(Dispatchers.IO) { // <- extension on current scope
237  *      ... load some UI data for the Main thread ...
238  *     }
239  *
240  *     withContext(Dispatchers.Main) {
241  *         doSomeWork()
242  *         val result = data.await()
243  *         display(result)
244  *     }
245  * }
246  * ```
247  *
248  * The scope in this example has the following semantics:
249  * 1) `showSomeData` returns as soon as the data is loaded and displayed in the UI.
250  * 2) If `doSomeWork` throws an exception, then the `async` task is cancelled and `showSomeData` rethrows that exception.
251  * 3) If the outer scope of `showSomeData` is cancelled, both started `async` and `withContext` blocks are cancelled.
252  * 4) If the `async` block fails, `withContext` will be cancelled.
253  *
254  * The method may throw a [CancellationException] if the current job was cancelled externally
255  * or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope
256  * (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope).
257  */
coroutineScopenull258 public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
259     contract {
260         callsInPlace(block, InvocationKind.EXACTLY_ONCE)
261     }
262     return suspendCoroutineUninterceptedOrReturn { uCont ->
263         val coroutine = ScopeCoroutine(uCont.context, uCont)
264         coroutine.startUndispatchedOrReturn(coroutine, block)
265     }
266 }
267 
268 /**
269  * Creates a [CoroutineScope] that wraps the given coroutine [context].
270  *
271  * If the given [context] does not contain a [Job] element, then a default `Job()` is created.
272  * This way, failure of any child coroutine in this scope or [cancellation][CoroutineScope.cancel] of the scope itself
273  * cancels all the scope's children, just like inside [coroutineScope] block.
274  */
275 @Suppress("FunctionName")
CoroutineScopenull276 public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
277     ContextScope(if (context[Job] != null) context else context + Job())
278 
279 /**
280  * Cancels this scope, including its job and all its children with an optional cancellation [cause].
281  * A cause can be used to specify an error message or to provide other details on
282  * a cancellation reason for debugging purposes.
283  * Throws [IllegalStateException] if the scope does not have a job in it.
284  */
285 public fun CoroutineScope.cancel(cause: CancellationException? = null) {
286     val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
287     job.cancel(cause)
288 }
289 
290 /**
291  * Cancels this scope, including its job and all its children with a specified diagnostic error [message].
292  * A [cause] can be specified to provide additional details on a cancellation reason for debugging purposes.
293  * Throws [IllegalStateException] if the scope does not have a job in it.
294  */
cancelnull295 public fun CoroutineScope.cancel(message: String, cause: Throwable? = null): Unit = cancel(CancellationException(message, cause))
296 
297 /**
298  * Ensures that current scope is [active][CoroutineScope.isActive].
299  *
300  * If the job is no longer active, throws [CancellationException].
301  * If the job was cancelled, thrown exception contains the original cancellation cause.
302  * This function does not do anything if there is no [Job] in the scope's [coroutineContext][CoroutineScope.coroutineContext].
303  *
304  * This method is a drop-in replacement for the following code, but with more precise exception:
305  * ```
306  * if (!isActive) {
307  *     throw CancellationException()
308  * }
309  * ```
310  *
311  * @see CoroutineContext.ensureActive
312  */
313 public fun CoroutineScope.ensureActive(): Unit = coroutineContext.ensureActive()
314 
315 
316 /**
317  * Returns the current [CoroutineContext] retrieved by using [kotlin.coroutines.coroutineContext].
318  * This function is an alias to avoid name clash with [CoroutineScope.coroutineContext] in a receiver position:
319  *
320  * ```
321  * launch { // this: CoroutineScope
322  *     val flow = flow<Unit> {
323  *         coroutineContext // Resolves into the context of outer launch, which is incorrect, see KT-38033
324  *         currentCoroutineContext() // Retrieves actual context where the flow is collected
325  *     }
326  * }
327  * ```
328  */
329 public suspend inline fun currentCoroutineContext(): CoroutineContext = coroutineContext
330