• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlinx.coroutines.internal.*
8 import kotlinx.coroutines.intrinsics.*
9 import kotlin.coroutines.*
10 import kotlin.coroutines.intrinsics.*
11 
12 /**
13  * Defines a scope for new coroutines. Every coroutine builder
14  * is an extension on [CoroutineScope] and inherits its [coroutineContext][CoroutineScope.coroutineContext]
15  * to automatically propagate both context elements and cancellation.
16  *
17  * The best ways to obtain a standalone instance of the scope are [CoroutineScope()] and [MainScope()] factory functions.
18  * Additional context elements can be appended to the scope using the [plus][CoroutineScope.plus] operator.
19  *
20  * Manual implementation of this interface is not recommended, implementation by delegation should be preferred instead.
21  * By convention, the [context of a scope][CoroutineScope.coroutineContext] should contain an instance of a [job][Job] to enforce structured concurrency.
22  *
23  * Every coroutine builder (like [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc)
24  * and every scoping function (like [coroutineScope], [withContext], etc) provides _its own_ scope
25  * with its own [Job] instance into the inner block of code it runs.
26  * By convention, they all wait for all the coroutines inside their block to complete before completing themselves,
27  * thus enforcing the discipline of **structured concurrency**.
28  *
29  * [CoroutineScope] should be implemented (or used as a field) on entities with a well-defined lifecycle that are responsible
30  * for launching children coroutines. Example of such entity on Android is Activity.
31  * Usage of this interface may look like this:
32  *
33  * ```
34  * class MyActivity : AppCompatActivity(), CoroutineScope by MainScope() {
35  *     override fun onDestroy() {
36  *         cancel() // cancel is extension on CoroutineScope
37  *     }
38  *
39  *     /*
40  *      * Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
41  *      * in this method throws an exception, then all nested coroutines are cancelled.
42  *      */
43  *     fun showSomeData() = launch { // <- extension on current activity, launched in the main thread
44  *        // ... here we can use suspending functions or coroutine builders with other dispatchers
45  *        draw(data) // draw in the main thread
46  *     }
47  * }
48  * ```
49  */
50 public interface CoroutineScope {
51     /**
52      * The context of this scope.
53      * Context is encapsulated by the scope and used for implementation of coroutine builders that are extensions on the scope.
54      * Accessing this property in general code is not recommended for any purposes except accessing the [Job] instance for advanced usages.
55      *
56      * By convention, should contain an instance of a [job][Job] to enforce structured concurrency.
57      */
58     public val coroutineContext: CoroutineContext
59 }
60 
61 /**
62  * Adds the specified coroutine context to this scope, overriding existing elements in the current
63  * scope's context with the corresponding keys.
64  *
65  * This is a shorthand for `CoroutineScope(thisScope + context)`.
66  */
plusnull67 public operator fun CoroutineScope.plus(context: CoroutineContext): CoroutineScope =
68     ContextScope(coroutineContext + context)
69 
70 /**
71  * Creates the main [CoroutineScope] for UI components.
72  *
73  * Example of use:
74  * ```
75  * class MyAndroidActivity {
76  *   private val scope = MainScope()
77  *
78  *   override fun onDestroy() {
79  *     super.onDestroy()
80  *     scope.cancel()
81  *   }
82  * }
83  *
84  * ```
85  *
86  * The resulting scope has [SupervisorJob] and [Dispatchers.Main] context elements.
87  * If you want to append additional elements to the main scope, use [CoroutineScope.plus] operator:
88  * `val scope = MainScope() + CoroutineName("MyActivity")`.
89  */
90 @Suppress("FunctionName")
91 public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
92 
93 /**
94  * Returns `true` when the current [Job] is still active (has not completed and was not cancelled yet).
95  *
96  * Check this property in long-running computation loops to support cancellation:
97  * ```
98  * while (isActive) {
99  *     // do some computation
100  * }
101  * ```
102  *
103  * This property is a shortcut for `coroutineContext.isActive` in the scope when
104  * [CoroutineScope] is available.
105  * See [coroutineContext][kotlin.coroutines.coroutineContext],
106  * [isActive][kotlinx.coroutines.isActive] and [Job.isActive].
107  */
108 @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
109 public val CoroutineScope.isActive: Boolean
110     get() = coroutineContext[Job]?.isActive ?: true
111 
112 /**
113  * A global [CoroutineScope] not bound to any job.
114  *
115  * Global scope is used to launch top-level coroutines which are operating on the whole application lifetime
116  * and are not cancelled prematurely.
117  * Another use of the global scope is operators running in [Dispatchers.Unconfined], which don't have any job associated with them.
118  *
119  * Application code usually should use an application-defined [CoroutineScope]. Using
120  * [async][CoroutineScope.async] or [launch][CoroutineScope.launch]
121  * on the instance of [GlobalScope] is highly discouraged.
122  *
123  * Usage of this interface may look like this:
124  *
125  * ```
126  * fun ReceiveChannel<Int>.sqrt(): ReceiveChannel<Double> = GlobalScope.produce(Dispatchers.Unconfined) {
127  *     for (number in this) {
128  *         send(Math.sqrt(number))
129  *     }
130  * }
131  *
132  * ```
133  */
134 public object GlobalScope : CoroutineScope {
135     /**
136      * Returns [EmptyCoroutineContext].
137      */
138     override val coroutineContext: CoroutineContext
139         get() = EmptyCoroutineContext
140 }
141 
142 /**
143  * Creates a [CoroutineScope] and calls the specified suspend block with this scope.
144  * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides
145  * the context's [Job].
146  *
147  * This function is designed for _parallel decomposition_ of work. When any child coroutine in this scope fails,
148  * this scope fails and all the rest of the children are cancelled (for a different behavior see [supervisorScope]).
149  * This function returns as soon as the given block and all its children coroutines are completed.
150  * A usage example of a scope looks like this:
151  *
152  * ```
153  * suspend fun showSomeData() = coroutineScope {
154  *
155  *   val data = async(Dispatchers.IO) { // <- extension on current scope
156  *      ... load some UI data for the Main thread ...
157  *   }
158  *
159  *   withContext(Dispatchers.Main) {
160  *     doSomeWork()
161  *     val result = data.await()
162  *     display(result)
163  *   }
164  * }
165  * ```
166  *
167  * The scope in this example has the following semantics:
168  * 1) `showSomeData` returns as soon as the data is loaded and displayed in the UI.
169  * 2) If `doSomeWork` throws an exception, then the `async` task is cancelled and `showSomeData` rethrows that exception.
170  * 3) If the outer scope of `showSomeData` is cancelled, both started `async` and `withContext` blocks are cancelled.
171  * 4) If the `async` block fails, `withContext` will be cancelled.
172  *
173  * The method may throw a [CancellationException] if the current job was cancelled externally
174  * or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope
175  * (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope).
176  */
coroutineScopenull177 public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R =
178     suspendCoroutineUninterceptedOrReturn { uCont ->
179         val coroutine = ScopeCoroutine(uCont.context, uCont)
180         coroutine.startUndispatchedOrReturn(coroutine, block)
181     }
182 
183 /**
184  * Creates a [CoroutineScope] that wraps the given coroutine [context].
185  *
186  * If the given [context] does not contain a [Job] element, then a default `Job()` is created.
187  * This way, cancellation or failure or any child coroutine in this scope cancels all the other children,
188  * just like inside [coroutineScope] block.
189  */
190 @Suppress("FunctionName")
CoroutineScopenull191 public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
192     ContextScope(if (context[Job] != null) context else context + Job())
193 
194 /**
195  * Cancels this scope, including its job and all its children with an optional cancellation [cause].
196  * A cause can be used to specify an error message or to provide other details on
197  * a cancellation reason for debugging purposes.
198  * Throws [IllegalStateException] if the scope does not have a job in it.
199  */
200 public fun CoroutineScope.cancel(cause: CancellationException? = null) {
201     val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
202     job.cancel(cause)
203 }
204 
205 /**
206  * Cancels this scope, including its job and all its children with a specified diagnostic error [message].
207  * A [cause] can be specified to provide additional details on a cancellation reason for debugging purposes.
208  * Throws [IllegalStateException] if the scope does not have a job in it.
209  */
cancelnull210 public fun CoroutineScope.cancel(message: String, cause: Throwable? = null): Unit = cancel(CancellationException(message, cause))
211 
212 /**
213  * Ensures that current scope is [active][CoroutineScope.isActive].
214  * Throws [IllegalStateException] if the context does not have a job in it.
215  *
216  * If the job is no longer active, throws [CancellationException].
217  * If the job was cancelled, thrown exception contains the original cancellation cause.
218  *
219  * This method is a drop-in replacement for the following code, but with more precise exception:
220  * ```
221  * if (!isActive) {
222  *     throw CancellationException()
223  * }
224  * ```
225  */
226 public fun CoroutineScope.ensureActive(): Unit = coroutineContext.ensureActive()
227