-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
Job.kt
676 lines (613 loc) · 29.9 KB
/
Job.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
@file:JvmMultifileClass
@file:JvmName("JobKt")
@file:Suppress("DEPRECATION_ERROR", "RedundantUnitReturnType")
package kotlinx.coroutines
import kotlinx.coroutines.selects.*
import kotlin.coroutines.*
import kotlin.jvm.*
// --------------- core job interfaces ---------------
/**
* A background job. Conceptually, a job is a cancellable thing with a life-cycle that
* culminates in its completion.
*
* Jobs can be arranged into parent-child hierarchies where cancellation
* of a parent leads to immediate cancellation of all its [children] recursively.
* Failure of a child with an exception other than [CancellationException] immediately cancels its parent and,
* consequently, all its other children. This behavior can be customized using [SupervisorJob].
*
* The most basic instances of `Job` interface are created like this:
*
* - **Coroutine job** is created with [launch][CoroutineScope.launch] coroutine builder.
* It runs a specified block of code and completes on completion of this block.
* - **[CompletableJob]** is created with a `Job()` factory function.
* It is completed by calling [CompletableJob.complete].
*
* Conceptually, an execution of a job does not produce a result value. Jobs are launched solely for their
* side effects. See [Deferred] interface for a job that produces a result.
*
* ### Job states
*
* A job has the following states:
*
* | **State** | [isActive] | [isCompleted] | [isCancelled] |
* | -------------------------------- | ---------- | ------------- | ------------- |
* | _New_ (optional initial state) | `false` | `false` | `false` |
* | _Active_ (default initial state) | `true` | `false` | `false` |
* | _Completing_ (transient state) | `true` | `false` | `false` |
* | _Cancelling_ (transient state) | `false` | `false` | `true` |
* | _Cancelled_ (final state) | `false` | `true` | `true` |
* | _Completed_ (final state) | `false` | `true` | `false` |
*
* Usually, a job is created in the _active_ state (it is created and started). However, coroutine builders
* that provide an optional `start` parameter create a coroutine in the _new_ state when this parameter is set to
* [CoroutineStart.LAZY]. Such a job can be made _active_ by invoking [start] or [join].
*
* A job is _active_ while the coroutine is working or until [CompletableJob] is completed,
* or until it fails or cancelled.
*
* Failure of an _active_ job with an exception makes it _cancelling_.
* A job can be cancelled at any time with [cancel] function that forces it to transition to
* the _cancelling_ state immediately. The job becomes _cancelled_ when it finishes executing its work and
* all its children complete.
*
* Completion of an _active_ coroutine's body or a call to [CompletableJob.complete] transitions the job to
* the _completing_ state. It waits in the _completing_ state for all its children to complete before
* transitioning to the _completed_ state.
* Note that _completing_ state is purely internal to the job. For an outside observer a _completing_ job is still
* active, while internally it is waiting for its children.
*
* ```
* wait children
* +-----+ start +--------+ complete +-------------+ finish +-----------+
* | New | -----> | Active | ---------> | Completing | -------> | Completed |
* +-----+ +--------+ +-------------+ +-----------+
* | cancel / fail |
* | +----------------+
* | |
* V V
* +------------+ finish +-----------+
* | Cancelling | --------------------------------> | Cancelled |
* +------------+ +-----------+
* ```
*
* A `Job` instance in the
* [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/coroutine-context.html)
* represents the coroutine itself.
*
* ### Cancellation cause
*
* A coroutine job is said to _complete exceptionally_ when its body throws an exception;
* a [CompletableJob] is completed exceptionally by calling [CompletableJob.completeExceptionally].
* An exceptionally completed job is cancelled and the corresponding exception becomes the _cancellation cause_ of the job.
*
* Normal cancellation of a job is distinguished from its failure by the type of this exception that caused its cancellation.
* A coroutine that threw [CancellationException] is considered to be _cancelled normally_.
* If a cancellation cause is a different exception type, then the job is considered to have _failed_.
* When a job has _failed_, then its parent gets cancelled with the exception of the same type,
* thus ensuring transparency in delegating parts of the job to its children.
*
* Note, that [cancel] function on a job only accepts [CancellationException] as a cancellation cause, thus
* calling [cancel] always results in a normal cancellation of a job, which does not lead to cancellation
* of its parent. This way, a parent can [cancel] its own children (cancelling all their children recursively, too)
* without cancelling itself.
*
* ### Concurrency and synchronization
*
* All functions on this interface and on all interfaces derived from it are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
*/
@OptIn(ExperimentalSubclassOptIn::class)
@SubclassOptInRequired(markerClass = InternalForInheritanceCoroutinesApi::class)
public interface Job : CoroutineContext.Element {
/**
* Key for [Job] instance in the coroutine context.
*/
public companion object Key : CoroutineContext.Key<Job>
// ------------ state query ------------
/**
* Returns the parent of the current job if the parent-child relationship
* is established or `null` if the job has no parent or was successfully completed.
*
* Accesses to this property are not idempotent, the property becomes `null` as soon
* as the job is transitioned to its final state, whether it is cancelled or completed,
* and all job children are completed.
*
* For a coroutine, its corresponding job completes as soon as the coroutine itself
* and all its children are complete.
*
* @see [Job] state transitions for additional details.
*/
@ExperimentalCoroutinesApi
public val parent: Job?
/**
* Returns `true` when this job is active -- it was already started and has not completed nor was cancelled yet.
* The job that is waiting for its [children] to complete is still considered to be active if it
* was not cancelled nor failed.
*
* See [Job] documentation for more details on job states.
*/
public val isActive: Boolean
/**
* Returns `true` when this job has completed for any reason. A job that was cancelled or failed
* and has finished its execution is also considered complete. Job becomes complete only after
* all its [children] complete.
*
* See [Job] documentation for more details on job states.
*/
public val isCompleted: Boolean
/**
* Returns `true` if this job was cancelled for any reason, either by explicit invocation of [cancel] or
* because it had failed or its child or parent was cancelled.
* In the general case, it does not imply that the
* job has already [completed][isCompleted], because it may still be finishing whatever it was doing and
* waiting for its [children] to complete.
*
* See [Job] documentation for more details on cancellation and failures.
*/
public val isCancelled: Boolean
/**
* Returns [CancellationException] that signals the completion of this job. This function is
* used by [cancellable][suspendCancellableCoroutine] suspending functions. They throw exception
* returned by this function when they suspend in the context of this job and this job becomes _complete_.
*
* This function returns the original [cancel] cause of this job if that `cause` was an instance of
* [CancellationException]. Otherwise (if this job was cancelled with a cause of a different type, or
* was cancelled without a cause, or had completed normally), an instance of [CancellationException] is
* returned. The [CancellationException.cause] of the resulting [CancellationException] references
* the original cancellation cause that was passed to [cancel] function.
*
* This function throws [IllegalStateException] when invoked on a job that is still active.
*
* @suppress **This an internal API and should not be used from general code.**
*/
@InternalCoroutinesApi
public fun getCancellationException(): CancellationException
// ------------ state update ------------
/**
* Starts coroutine related to this job (if any) if it was not started yet.
* The result is `true` if this invocation actually started coroutine or `false`
* if it was already started or completed.
*/
public fun start(): Boolean
/**
* Cancels this job with an optional cancellation [cause].
* A cause can be used to specify an error message or to provide other details on
* the cancellation reason for debugging purposes.
* See [Job] documentation for full explanation of cancellation machinery.
*/
public fun cancel(cause: CancellationException? = null)
/**
* @suppress This method implements old version of JVM ABI. Use [cancel].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun cancel(): Unit = cancel(null)
/**
* @suppress This method has bad semantics when cause is not a [CancellationException]. Use [cancel].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun cancel(cause: Throwable? = null): Boolean
// ------------ parent-child ------------
/**
* Returns a sequence of this job's children.
*
* A job becomes a child of this job when it is constructed with this job in its
* [CoroutineContext] or using an explicit `parent` parameter.
*
* A parent-child relation has the following effect:
*
* - Cancellation of parent with [cancel] or its exceptional completion (failure)
* immediately cancels all its children.
* - Parent cannot complete until all its children are complete. Parent waits for all its children to
* complete in _completing_ or _cancelling_ state.
* - Uncaught exception in a child, by default, cancels parent. This applies even to
* children created with [async][CoroutineScope.async] and other future-like
* coroutine builders, even though their exceptions are caught and are encapsulated in their result.
* This default behavior can be overridden with [SupervisorJob].
*/
public val children: Sequence<Job>
/**
* Attaches child job so that this job becomes its parent and
* returns a handle that should be used to detach it.
*
* A parent-child relation has the following effect:
* - Cancellation of parent with [cancel] or its exceptional completion (failure)
* immediately cancels all its children.
* - Parent cannot complete until all its children are complete. Parent waits for all its children to
* complete in _completing_ or _cancelling_ states.
*
* **A child must store the resulting [ChildHandle] and [dispose][DisposableHandle.dispose] the attachment
* to its parent on its own completion.**
*
* Coroutine builders and job factory functions that accept `parent` [CoroutineContext] parameter
* lookup a [Job] instance in the parent context and use this function to attach themselves as a child.
* They also store a reference to the resulting [ChildHandle] and dispose a handle when they complete.
*
* @suppress This is an internal API. This method is too error prone for public API.
*/
// ChildJob and ChildHandle are made internal on purpose to further deter 3rd-party impl of Job
@InternalCoroutinesApi
public fun attachChild(child: ChildJob): ChildHandle
// ------------ state waiting ------------
/**
* Suspends the coroutine until this job is complete. This invocation resumes normally (without exception)
* when the job is complete for any reason and the [Job] of the invoking coroutine is still [active][isActive].
* This function also [starts][Job.start] the corresponding coroutine if the [Job] was still in _new_ state.
*
* Note that the job becomes complete only when all its children are complete.
*
* This suspending function is cancellable and **always** checks for a cancellation of the invoking coroutine's Job.
* If the [Job] of the invoking coroutine is cancelled or completed when this
* suspending function is invoked or while it is suspended, this function
* throws [CancellationException].
*
* In particular, it means that a parent coroutine invoking `join` on a child coroutine throws
* [CancellationException] if the child had failed, since a failure of a child coroutine cancels parent by default,
* unless the child was launched from within [supervisorScope].
*
* This function can be used in [select] invocation with [onJoin] clause.
* Use [isCompleted] to check for a completion of this job without waiting.
*
* There is [cancelAndJoin] function that combines an invocation of [cancel] and `join`.
*/
public suspend fun join()
/**
* Clause for [select] expression of [join] suspending function that selects when the job is complete.
* This clause never fails, even if the job completes exceptionally.
*/
public val onJoin: SelectClause0
// ------------ low-level state-notification ------------
/**
* Registers handler that is **synchronously** invoked once on completion of this job.
* When the job is already complete, then the handler is immediately invoked
* with the job's exception or cancellation cause or `null`. Otherwise, the handler will be invoked once when this
* job is complete.
*
* The meaning of `cause` that is passed to the handler:
* - Cause is `null` when the job has completed normally.
* - Cause is an instance of [CancellationException] when the job was cancelled _normally_.
* **It should not be treated as an error**. In particular, it should not be reported to error logs.
* - Otherwise, the job had _failed_.
*
* The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the
* registration of this handler and release its memory if its invocation is no longer needed.
* There is no need to dispose the handler after completion of this job. The references to
* all the handlers are released when this job completes.
*
* Installed [handler] should not throw any exceptions. If it does, they will get caught,
* wrapped into [CompletionHandlerException], and rethrown, potentially causing crash of unrelated code.
*
* **Note**: Implementation of `CompletionHandler` must be fast, non-blocking, and thread-safe.
* This handler can be invoked concurrently with the surrounding code.
* There is no guarantee on the execution context in which the [handler] is invoked.
*/
public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
/**
* Kept for preserving compatibility. Shouldn't be used by anyone.
* @suppress
*/
@InternalCoroutinesApi
public fun invokeOnCompletion(
onCancelling: Boolean = false,
invokeImmediately: Boolean = true,
handler: CompletionHandler): DisposableHandle
// ------------ unstable internal API ------------
/**
* @suppress **Error**: Operator '+' on two Job objects is meaningless.
* Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts.
* The job to the right of `+` just replaces the job the left of `+`.
*/
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated(message = "Operator '+' on two Job objects is meaningless. " +
"Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " +
"The job to the right of `+` just replaces the job the left of `+`.",
level = DeprecationLevel.ERROR)
public operator fun plus(other: Job): Job = other
}
/**
* Registers a handler that is **synchronously** invoked once on cancellation or completion of this job.
*
* If the handler would have been invoked earlier if it was registered at that time, then it is invoked immediately,
* unless [invokeImmediately] is set to `false`.
*
* The meaning of `cause` that is passed to the handler is:
* - It is `null` if the job has completed normally.
* - It is an instance of [CancellationException] if the job was cancelled _normally_.
* **It should not be treated as an error**. In particular, it should not be reported to error logs.
* - Otherwise, the job had _failed_.
*
* The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of the registration of this
* handler and release its memory if its invocation is no longer needed.
* There is no need to dispose of the handler after completion of this job. The references to
* all the handlers are released when this job completes.
*/
internal fun Job.invokeOnCompletion(
invokeImmediately: Boolean = true,
handler: JobNode,
): DisposableHandle = when (this) {
is JobSupport -> invokeOnCompletionInternal(invokeImmediately, handler)
else -> invokeOnCompletion(handler.onCancelling, invokeImmediately, handler::invoke)
}
/**
* Creates a job object in an active state.
* A failure of any child of this job immediately causes this job to fail, too, and cancels the rest of its children.
*
* To handle children failure independently of each other use [SupervisorJob].
*
* If [parent] job is specified, then this job becomes a child job of its parent and
* is cancelled when its parent fails or is cancelled. All this job's children are cancelled in this case, too.
*
* Conceptually, the resulting job works in the same way as the job created by the `launch { body }` invocation
* (see [launch]), but without any code in the body. It is active until cancelled or completed. Invocation of
* [CompletableJob.complete] or [CompletableJob.completeExceptionally] corresponds to the successful or
* failed completion of the body of the coroutine.
*
* @param parent an optional parent job.
*/
@Suppress("FunctionName")
public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)
/** @suppress Binary compatibility only */
@Suppress("FunctionName")
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
@JvmName("Job")
public fun Job0(parent: Job? = null): Job = Job(parent)
/**
* A handle to an allocated object that can be disposed to make it eligible for garbage collection.
*/
public fun interface DisposableHandle {
/**
* Disposes the corresponding object, making it eligible for garbage collection.
* Repeated invocation of this function has no effect.
*/
public fun dispose()
}
// -------------------- Parent-child communication --------------------
/**
* A reference that parent receives from its child so that it can report its cancellation.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
@OptIn(InternalForInheritanceCoroutinesApi::class)
public interface ChildJob : Job {
/**
* Parent is cancelling its child by invoking this method.
* Child finds the cancellation cause using [ParentJob.getChildJobCancellationCause].
* This method does nothing is the child is already being cancelled.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
public fun parentCancelled(parentJob: ParentJob)
}
/**
* A reference that child receives from its parent when it is being cancelled by the parent.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
@OptIn(InternalForInheritanceCoroutinesApi::class)
public interface ParentJob : Job {
/**
* Child job is using this method to learn its cancellation cause when the parent cancels it with [ChildJob.parentCancelled].
* This method is invoked only if the child was not already being cancelled.
*
* Note that [CancellationException] is the method's return type: if child is cancelled by its parent,
* then the original exception is **already** handled by either the parent or the original source of failure.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
public fun getChildJobCancellationCause(): CancellationException
}
/**
* A handle that child keep onto its parent so that it is able to report its cancellation.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
public interface ChildHandle : DisposableHandle {
/**
* Returns the parent of the current parent-child relationship.
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
public val parent: Job?
/**
* Child is cancelling its parent by invoking this method.
* This method is invoked by the child twice. The first time child report its root cause as soon as possible,
* so that all its siblings and the parent can start cancelling their work asap. The second time
* child invokes this method when it had aggregated and determined its final cancellation cause.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
public fun childCancelled(cause: Throwable): Boolean
}
// -------------------- Job extensions --------------------
/**
* Disposes a specified [handle] when this job is complete.
*
* This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
* ```
* invokeOnCompletion { handle.dispose() }
* ```
*/
internal fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle =
invokeOnCompletion(handler = DisposeOnCompletion(handle))
/**
* Cancels the job and suspends the invoking coroutine until the cancelled job is complete.
*
* This suspending function is cancellable and **always** checks for a cancellation of the invoking coroutine's Job.
* If the [Job] of the invoking coroutine is cancelled or completed when this
* suspending function is invoked or while it is suspended, this function
* throws [CancellationException].
*
* In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine throws
* [CancellationException] if the child had failed, since a failure of a child coroutine cancels parent by default,
* unless the child was launched from within [supervisorScope].
*
* This is a shortcut for the invocation of [cancel][Job.cancel] followed by [join][Job.join].
*/
public suspend fun Job.cancelAndJoin() {
cancel()
return join()
}
/**
* Cancels all [children][Job.children] jobs of this coroutine using [Job.cancel] for all of them
* with an optional cancellation [cause].
* Unlike [Job.cancel] on this job as a whole, the state of this job itself is not affected.
*/
public fun Job.cancelChildren(cause: CancellationException? = null) {
children.forEach { it.cancel(cause) }
}
/**
* @suppress This method implements old version of JVM ABI. Use [cancel].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun Job.cancelChildren(): Unit = cancelChildren(null)
/**
* @suppress This method has bad semantics when cause is not a [CancellationException]. Use [Job.cancelChildren].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun Job.cancelChildren(cause: Throwable? = null) {
children.forEach { (it as? JobSupport)?.cancelInternal(cause.orCancellation(this)) }
}
// -------------------- CoroutineContext extensions --------------------
/**
* Returns `true` when the [Job] of the coroutine in this context is still active
* (has not completed and was not cancelled yet) or the context does not have a [Job] in it.
*
* Check this property in long-running computation loops to support cancellation
* when [CoroutineScope.isActive] is not available:
*
* ```
* while (coroutineContext.isActive) {
* // do some computation
* }
* ```
*
* The `coroutineContext.isActive` expression is a shortcut for `get(Job)?.isActive ?: true`.
* See [Job.isActive].
*/
public val CoroutineContext.isActive: Boolean
get() = get(Job)?.isActive ?: true
/**
* Cancels [Job] of this context with an optional cancellation cause.
* See [Job.cancel] for details.
*/
public fun CoroutineContext.cancel(cause: CancellationException? = null) {
this[Job]?.cancel(cause)
}
/**
* @suppress This method implements old version of JVM ABI. Use [CoroutineContext.cancel].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun CoroutineContext.cancel(): Unit = cancel(null)
/**
* Ensures that current job is [active][Job.isActive].
* If the job is no longer active, throws [CancellationException].
* If the job was cancelled, thrown exception contains the original cancellation cause.
*
* This method is a drop-in replacement for the following code, but with more precise exception:
* ```
* if (!job.isActive) {
* throw CancellationException()
* }
* ```
*/
public fun Job.ensureActive(): Unit {
if (!isActive) throw getCancellationException()
}
/**
* Ensures that job in the current context is [active][Job.isActive].
*
* If the job is no longer active, throws [CancellationException].
* If the job was cancelled, thrown exception contains the original cancellation cause.
* This function does not do anything if there is no [Job] in the context, since such a coroutine cannot be cancelled.
*
* This method is a drop-in replacement for the following code, but with more precise exception:
* ```
* if (!isActive) {
* throw CancellationException()
* }
* ```
*/
public fun CoroutineContext.ensureActive() {
get(Job)?.ensureActive()
}
/**
* Cancels current job, including all its children with a specified diagnostic error [message].
* A [cause] can be specified to provide additional details on a cancellation reason for debugging purposes.
*/
public fun Job.cancel(message: String, cause: Throwable? = null): Unit = cancel(CancellationException(message, cause))
/**
* @suppress This method has bad semantics when cause is not a [CancellationException]. Use [CoroutineContext.cancel].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun CoroutineContext.cancel(cause: Throwable? = null): Boolean {
val job = this[Job] as? JobSupport ?: return false
job.cancelInternal(cause.orCancellation(job))
return true
}
/**
* Cancels all children of the [Job] in this context, without touching the state of this job itself
* with an optional cancellation cause. See [Job.cancel].
* It does not do anything if there is no job in the context or it has no children.
*/
public fun CoroutineContext.cancelChildren(cause: CancellationException? = null) {
this[Job]?.children?.forEach { it.cancel(cause) }
}
/**
* @suppress This method implements old version of JVM ABI. Use [CoroutineContext.cancelChildren].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun CoroutineContext.cancelChildren(): Unit = cancelChildren(null)
/**
* Retrieves the current [Job] instance from the given [CoroutineContext] or
* throws [IllegalStateException] if no job is present in the context.
*
* This method is a short-cut for `coroutineContext[Job]!!` and should be used only when it is known in advance that
* the context does have instance of the job in it.
*/
public val CoroutineContext.job: Job get() = get(Job) ?: error("Current context doesn't contain Job in it: $this")
/**
* @suppress This method has bad semantics when cause is not a [CancellationException]. Use [CoroutineContext.cancelChildren].
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun CoroutineContext.cancelChildren(cause: Throwable? = null) {
val job = this[Job] ?: return
job.children.forEach { (it as? JobSupport)?.cancelInternal(cause.orCancellation(job)) }
}
private fun Throwable?.orCancellation(job: Job): Throwable = this ?: JobCancellationException("Job was cancelled", null, job)
/**
* No-op implementation of [DisposableHandle].
* @suppress **This an internal API and should not be used from general code.**
*/
@InternalCoroutinesApi
public object NonDisposableHandle : DisposableHandle, ChildHandle {
override val parent: Job? get() = null
/**
* Does not do anything.
* @suppress
*/
override fun dispose() {}
/**
* Returns `false`.
* @suppress
*/
override fun childCancelled(cause: Throwable): Boolean = false
/**
* Returns "NonDisposableHandle" string.
* @suppress
*/
override fun toString(): String = "NonDisposableHandle"
}
private class DisposeOnCompletion(
private val handle: DisposableHandle
) : JobNode() {
override val onCancelling get() = false
override fun invoke(cause: Throwable?) = handle.dispose()
}