1 /*
2  * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 @file:Suppress("DEPRECATION_ERROR")
5 
6 package kotlinx.coroutines
7 
8 import kotlinx.coroutines.CoroutineStart.*
9 import kotlinx.coroutines.intrinsics.*
10 import kotlin.coroutines.*
11 import kotlin.jvm.*
12 
13 /**
14  * Abstract base class for implementation of coroutines in coroutine builders.
15  *
16  * This class implements completion [Continuation], [Job], and [CoroutineScope] interfaces.
17  * It stores the result of continuation in the state of the job.
18  * This coroutine waits for children coroutines to finish before completing and
19  * fails through an intermediate _failing_ state.
20  *
21  * The following methods are available for override:
22  *
23  * * [onStart] is invoked when the coroutine was created in non-active state and is being [started][Job.start].
24  * * [onCancelling] is invoked as soon as the coroutine starts being cancelled for any reason (or completes).
25  * * [onCompleted] is invoked when the coroutine completes with a value.
26  * * [onCancelled] in invoked when the coroutine completes with an exception (cancelled).
27  *
28  * @param parentContext the context of the parent coroutine.
29  * @param active when `true` (by default), the coroutine is created in the _active_ state, otherwise it is created in the _new_ state.
30  *               See [Job] for details.
31  *
32  * @suppress **This an internal API and should not be used from general code.**
33  */
34 @InternalCoroutinesApi
35 public abstract class AbstractCoroutine<in T>(
36     /**
37      * The context of the parent coroutine.
38      */
39     @JvmField
40     protected val parentContext: CoroutineContext,
41     active: Boolean = true
42 ) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
43     /**
44      * The context of this coroutine that includes this coroutine as a [Job].
45      */
46     @Suppress("LeakingThis")
47     public final override val context: CoroutineContext = parentContext + this
48 
49     /**
50      * The context of this scope which is the same as the [context] of this coroutine.
51      */
52     public override val coroutineContext: CoroutineContext get() = context
53 
54     override val isActive: Boolean get() = super.isActive
55 
56     /**
57      * Initializes the parent job from the `parentContext` of this coroutine that was passed to it during construction.
58      * It shall be invoked at most once after construction after all other initialization.
59      *
60      * Invocation of this function may cause this coroutine to become cancelled if the parent is already cancelled,
61      * in which case it synchronously invokes all the corresponding handlers.
62      * @suppress **This is unstable API and it is subject to change.**
63      */
initParentJobnull64     internal fun initParentJob() {
65         initParentJobInternal(parentContext[Job])
66     }
67 
68     /**
69      * This function is invoked once when a non-active coroutine (constructed with `active` set to `false)
70      * is [started][start].
71      */
onStartnull72     protected open fun onStart() {}
73 
onStartInternalnull74     internal final override fun onStartInternal() {
75         onStart()
76     }
77 
78     /**
79      * This function is invoked once when the job was completed normally with the specified [value],
80      * right before all the waiters for the coroutine's completion are notified.
81      */
onCompletednull82     protected open fun onCompleted(value: T) {}
83 
84     /**
85      * This function is invoked once when the job was cancelled with the specified [cause],
86      * right before all the waiters for coroutine's completion are notified.
87      *
88      * **Note:** the state of the coroutine might not be final yet in this function and should not be queried.
89      * You can use [completionCause] and [completionCauseHandled] to recover parameters that we passed
90      * to this `onCancelled` invocation only when [isCompleted] returns `true`.
91      *
92      * @param cause The cancellation (failure) cause
93      * @param handled `true` if the exception was handled by parent (always `true` when it is a [CancellationException])
94      */
onCancellednull95     protected open fun onCancelled(cause: Throwable, handled: Boolean) {}
96 
cancellationExceptionMessagenull97     override fun cancellationExceptionMessage(): String = "$classSimpleName was cancelled"
98 
99     @Suppress("UNCHECKED_CAST")
100     protected final override fun onCompletionInternal(state: Any?) {
101         if (state is CompletedExceptionally)
102             onCancelled(state.cause, state.handled)
103         else
104             onCompleted(state as T)
105     }
106 
107     /**
108      * Completes execution of this with coroutine with the specified result.
109      */
resumeWithnull110     public final override fun resumeWith(result: Result<T>) {
111         val state = makeCompletingOnce(result.toState())
112         if (state === COMPLETING_WAITING_CHILDREN) return
113         afterResume(state)
114     }
115 
afterResumenull116     protected open fun afterResume(state: Any?): Unit = afterCompletion(state)
117 
118     internal final override fun handleOnCompletionException(exception: Throwable) {
119         handleCoroutineException(context, exception)
120     }
121 
nameStringnull122     internal override fun nameString(): String {
123         val coroutineName = context.coroutineName ?: return super.nameString()
124         return "\"$coroutineName\":${super.nameString()}"
125     }
126 
127     /**
128      * Starts this coroutine with the given code [block] and [start] strategy.
129      * This function shall be invoked at most once on this coroutine.
130      *
131      * First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
132      * during construction. Second, it starts the coroutine based on [start] parameter:
133      *
134      * * [DEFAULT] uses [startCoroutineCancellable].
135      * * [ATOMIC] uses [startCoroutine].
136      * * [UNDISPATCHED] uses [startCoroutineUndispatched].
137      * * [LAZY] does nothing.
138      */
startnull139     public fun start(start: CoroutineStart, block: suspend () -> T) {
140         initParentJob()
141         start(block, this)
142     }
143 
144     /**
145      * Starts this coroutine with the given code [block] and [start] strategy.
146      * This function shall be invoked at most once on this coroutine.
147      *
148      * First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
149      * during construction. Second, it starts the coroutine based on [start] parameter:
150      *
151      * * [DEFAULT] uses [startCoroutineCancellable].
152      * * [ATOMIC] uses [startCoroutine].
153      * * [UNDISPATCHED] uses [startCoroutineUndispatched].
154      * * [LAZY] does nothing.
155      */
startnull156     public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
157         initParentJob()
158         start(block, receiver, this)
159     }
160 }
161