@@ -2,7 +2,6 @@ package kotlinx.coroutines.experimental
22
33import kotlinx.coroutines.experimental.internal.LockFreeLinkedListHead
44import kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode
5- import java.util.concurrent.CancellationException
65import java.util.concurrent.Future
76import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
87import kotlin.coroutines.AbstractCoroutineContextElement
@@ -28,7 +27,7 @@ public interface Job : CoroutineContext.Element {
2827 /* *
2928 * Creates new job object. It is optionally a child of a [parent] job.
3029 */
31- public operator fun invoke (parent : Job ? = null): Job = JobSupport (parent)
30+ public operator fun invoke (parent : Job ? = null): Job = JobImpl (parent)
3231 }
3332
3433 /* *
@@ -67,9 +66,12 @@ public interface Job : CoroutineContext.Element {
6766 }
6867}
6968
70- typealias CompletionHandler = (Throwable ? ) -> Unit
69+ public typealias CompletionHandler = (Throwable ? ) -> Unit
7170
72- typealias CancellationException = CancellationException
71+ /* *
72+ * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled while it is suspending.
73+ */
74+ public typealias CancellationException = java.util.concurrent.CancellationException
7375
7476/* *
7577 * Unregisters a specified [registration] when this job is complete.
@@ -118,30 +120,39 @@ public suspend fun Job.join() {
118120 * state and mare store addition state information for completed jobs, like their result values.
119121 */
120122@Suppress(" LeakingThis" )
121- public open class JobSupport (
122- parent : Job ? = null
123- ) : AbstractCoroutineContextElement(Job ), Job {
123+ public open class JobSupport : AbstractCoroutineContextElement (Job ), Job {
124124 // keeps a stack of cancel listeners or a special CANCELLED, other values denote completed scope
125125 @Volatile
126126 private var state: Any? = ActiveList () // will drop the list on cancel
127127
128- // directly pass HandlerNode to parent scope to optimize one closure object (see makeNode)
129- private val registration: Job .Registration ? = parent?.onCompletion( CancelOnCompletion (parent, this ))
128+ @Volatile
129+ private var registration: Job .Registration ? = null
130130
131131 protected companion object {
132132 @JvmStatic
133133 private val STATE : AtomicReferenceFieldUpdater <JobSupport , Any ?> =
134134 AtomicReferenceFieldUpdater .newUpdater(JobSupport ::class .java, Any ::class .java, " state" )
135135 }
136136
137+ // invoke at most once after construction after all other initialization
138+ protected fun initParentJob (parent : Job ? ) {
139+ if (parent == null ) return
140+ check(registration == null )
141+ // directly pass HandlerNode to parent scope to optimize one closure object (see makeNode)
142+ val newRegistration = parent.onCompletion(CancelOnCompletion (parent, this ))
143+ registration = newRegistration
144+ // now check our state _after_ registering (see updateState order of actions)
145+ if (state !is Active ) newRegistration.unregister()
146+ }
147+
137148 protected fun getState (): Any? = state
138149
139150 protected fun updateState (expect : Any , update : Any? ): Boolean {
140151 expect as ActiveList // assert type
141152 require(update !is Active ) // only active -> inactive transition is allowed
142153 if (! STATE .compareAndSet(this , expect, update)) return false
143154 // #1. Unregister from parent job
144- registration?.unregister()
155+ registration?.unregister() // volatile read registration _after_ state was updated
145156 // #2 Invoke completion handlers
146157 val reason = (update as ? CompletedExceptionally )?.cancelReason
147158 var completionException: Throwable ? = null
@@ -202,21 +213,26 @@ public open class JobSupport(
202213 private class ActiveList : LockFreeLinkedListHead (), Active
203214
204215 protected abstract class CompletedExceptionally {
205- abstract val cancelReason: Throwable ?
206- abstract val exception: Throwable
216+ abstract val cancelReason: Throwable // original reason or fresh CancellationException
217+ abstract val exception: Throwable // the exception to be thrown in continuation
207218 }
208219
209- protected class Cancelled (override val cancelReason : Throwable ? ) : CompletedExceptionally() {
220+ protected class Cancelled (specifiedReason : Throwable ? ) : CompletedExceptionally() {
221+ @Volatile
222+ private var _cancelReason = specifiedReason // materialize CancellationException on first need
223+
224+ override val cancelReason: Throwable get() =
225+ _cancelReason ? : // atomic read volatile var or else create new
226+ CancellationException ().also { _cancelReason = it }
227+
210228 @Volatile
211229 private var _exception : Throwable ? = null // convert reason to CancellationException on first need
230+
212231 override val exception: Throwable get() =
213- _exception ? : // atomic read volatile var or else
214- run {
215- val result = cancelReason as ? CancellationException ? :
216- CancellationException ().apply { if (cancelReason != null ) initCause(cancelReason) }
217- _exception = result
218- result
219- }
232+ _exception ? : // atomic read volatile var or else build new
233+ (cancelReason as ? CancellationException ? :
234+ CancellationException (cancelReason.message).apply { initCause(cancelReason) })
235+ .also { _exception = it }
220236 }
221237
222238 protected class Failed (override val exception : Throwable ) : CompletedExceptionally() {
@@ -288,3 +304,7 @@ private class RemoveOnCompletion(
288304 override fun invoke (reason : Throwable ? ) { node.remove() }
289305 override fun toString () = " RemoveOnCompletion[$node ]"
290306}
307+
308+ private class JobImpl (parent : Job ? = null ) : JobSupport() {
309+ init { initParentJob(parent) }
310+ }
0 commit comments