blob: 39e0d60f7fcca9040ff9d734bb85b95d75f5cfc8 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Roman Elizarov3754f952017-01-18 20:47:54 +030017package kotlinx.coroutines.experimental
18
Roman Elizarova5676592017-01-19 10:31:13 +030019import kotlinx.coroutines.experimental.internal.LockFreeLinkedListHead
20import kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode
Roman Elizarov3754f952017-01-18 20:47:54 +030021import java.util.concurrent.Future
22import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
Roman Elizarovea4a51b2017-01-31 12:01:25 +030023import kotlin.coroutines.experimental.AbstractCoroutineContextElement
24import kotlin.coroutines.experimental.Continuation
25import kotlin.coroutines.experimental.CoroutineContext
Roman Elizarov3754f952017-01-18 20:47:54 +030026
27// --------------- core job interfaces ---------------
28
29/**
Roman Elizarovb7c46de2017-02-08 12:35:24 +030030 * A background job.
31 * A job can be _cancelled_ at any time with [cancel] function that forces it to become _completed_ immediately.
Roman Elizarov44ba4b12017-01-25 11:37:54 +030032 *
Roman Elizarovb7c46de2017-02-08 12:35:24 +030033 * It has two states:
34 * * _Active_ (initial state) -- [isActive] `true`,
35 * [getCompletionException] throws [IllegalStateException].
36 * * _Completed_ (final state) -- [isActive] `false`.
37 *
Roman Elizarov44ba4b12017-01-25 11:37:54 +030038 * A job in the coroutine [context][CoroutineScope.context] represents the coroutine itself.
Roman Elizarov3754f952017-01-18 20:47:54 +030039 * A job is active while the coroutine is working and job's cancellation aborts the coroutine when
40 * the coroutine is suspended on a _cancellable_ suspension point by throwing [CancellationException]
Roman Elizarovb7c46de2017-02-08 12:35:24 +030041 * or the cancellation cause inside the coroutine.
Roman Elizarov3754f952017-01-18 20:47:54 +030042 *
Roman Elizarov44ba4b12017-01-25 11:37:54 +030043 * A job can have a _parent_. A job with a parent is cancelled when its parent completes.
Roman Elizarov3754f952017-01-18 20:47:54 +030044 *
Roman Elizarovb7c46de2017-02-08 12:35:24 +030045 * All functions on this interface and on all interfaces derived from it are **thread-safe** and can
46 * be safely invoked from concurrent coroutines without external synchronization.
Roman Elizarov3754f952017-01-18 20:47:54 +030047 */
48public interface Job : CoroutineContext.Element {
Roman Elizarovd528e3e2017-01-23 15:40:05 +030049 /**
50 * Key for [Job] instance in the coroutine context.
51 */
Roman Elizarov3754f952017-01-18 20:47:54 +030052 public companion object Key : CoroutineContext.Key<Job> {
53 /**
54 * Creates new job object. It is optionally a child of a [parent] job.
55 */
Roman Elizarov58a7add2017-01-20 12:19:52 +030056 public operator fun invoke(parent: Job? = null): Job = JobImpl(parent)
Roman Elizarov3754f952017-01-18 20:47:54 +030057 }
58
59 /**
60 * Returns `true` when job is still active.
61 */
62 public val isActive: Boolean
63
64 /**
Roman Elizarovb7c46de2017-02-08 12:35:24 +030065 * Returns the exception that signals the completion of this job -- it returns the original
66 * [cancel] cause or an instance of [CancellationException] if this job had completed
67 * normally or was cancelled without a cause. This function throws
68 * [IllegalStateException] when invoked for an [active][isActive] job.
69 *
70 * The [cancellable][suspendCancellableCoroutine] suspending functions throw this exception
71 * when trying to suspend in the context of this job.
Roman Elizarov7cf452e2017-01-29 21:58:33 +030072 */
Roman Elizarovb7c46de2017-02-08 12:35:24 +030073 fun getCompletionException(): Throwable
Roman Elizarov7cf452e2017-01-29 21:58:33 +030074
75 /**
Roman Elizarov3754f952017-01-18 20:47:54 +030076 * Registers completion handler. The action depends on the state of this job.
77 * When job is cancelled with [cancel], then the handler is immediately invoked
Roman Elizarovb7c46de2017-02-08 12:35:24 +030078 * with a cancellation cause or with a fresh [CancellationException].
79 * Otherwise, handler will be invoked once when this job is complete
80 * (cancellation also is a form of completion).
81 *
Roman Elizarov3754f952017-01-18 20:47:54 +030082 * The resulting [Registration] can be used to [Registration.unregister] if this
83 * registration is no longer needed. There is no need to unregister after completion.
84 */
85 public fun onCompletion(handler: CompletionHandler): Registration
86
87 /**
Roman Elizarovf6fed2a2017-02-03 19:12:09 +030088 * Cancel this activity with an optional cancellation [cause]. The result is `true` if this job was
Roman Elizarov01934df2017-01-31 09:18:34 +030089 * cancelled as a result of this invocation and `false` otherwise
Roman Elizarovb7c46de2017-02-08 12:35:24 +030090 * (if it was already _completed_ or if it is [NonCancellable]).
91 * Repeated invocations of this function have no effect and always produce `false`.
Roman Elizarov01934df2017-01-31 09:18:34 +030092 *
Roman Elizarov3754f952017-01-18 20:47:54 +030093 * When cancellation has a clear reason in the code, an instance of [CancellationException] should be created
94 * at the corresponding original cancellation site and passed into this method to aid in debugging by providing
95 * both the context of cancellation and text description of the reason.
96 */
Roman Elizarovf6fed2a2017-02-03 19:12:09 +030097 public fun cancel(cause: Throwable? = null): Boolean
Roman Elizarov3754f952017-01-18 20:47:54 +030098
Roman Elizarov79a28682017-02-01 18:43:32 +030099 @Suppress("DeprecatedCallableAddReplaceWith")
100 @Deprecated(message = "Operator '+' on two Job objects is meaningless. " +
101 "Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " +
102 "The job to the right of `+` just replaces the job the left of `+`.",
103 level = DeprecationLevel.ERROR)
104 public operator fun plus(other: Job) = other
105
Roman Elizarov3754f952017-01-18 20:47:54 +0300106 /**
107 * Registration object for [onCompletion]. It can be used to [unregister] if needed.
108 * There is no need to unregister after completion.
109 */
110 public interface Registration {
111 /**
112 * Unregisters completion handler.
113 */
114 public fun unregister()
115 }
116}
117
Roman Elizarov58a7add2017-01-20 12:19:52 +0300118public typealias CompletionHandler = (Throwable?) -> Unit
Roman Elizarov3754f952017-01-18 20:47:54 +0300119
Roman Elizarov58a7add2017-01-20 12:19:52 +0300120/**
121 * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled while it is suspending.
122 */
123public typealias CancellationException = java.util.concurrent.CancellationException
Roman Elizarov3754f952017-01-18 20:47:54 +0300124
125/**
126 * Unregisters a specified [registration] when this job is complete.
127 * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
128 * ```
129 * onCompletion { registration.unregister() }
130 * ```
131 */
132public fun Job.unregisterOnCompletion(registration: Job.Registration): Job.Registration =
133 onCompletion(UnregisterOnCompletion(this, registration))
134
135/**
136 * Cancels a specified [future] when this job is complete.
137 * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
138 * ```
139 * onCompletion { future.cancel(true) }
140 * ```
141 */
142public fun Job.cancelFutureOnCompletion(future: Future<*>): Job.Registration =
143 onCompletion(CancelFutureOnCompletion(this, future))
144
Roman Elizarov53a0a402017-01-19 20:21:57 +0300145internal fun Job.removeOnCompletion(node: LockFreeLinkedListNode): Job.Registration =
146 onCompletion(RemoveOnCompletion(this, node))
147
Roman Elizarov3754f952017-01-18 20:47:54 +0300148/**
149 * Suspends coroutine until this job is complete. This invocation resumes normally (without exception)
150 * when the job is complete for any reason.
151 *
152 * This suspending function is cancellable. If the [Job] of the invoking coroutine is completed while this
153 * suspending function is suspended, this function immediately resumes with [CancellationException].
154 */
155public suspend fun Job.join() {
156 if (!isActive) return // fast path
157 return suspendCancellableCoroutine { cont ->
158 cont.unregisterOnCompletion(onCompletion(ResumeOnCompletion(this, cont)))
159 }
160}
161
162// --------------- utility classes to simplify job implementation
163
164/**
165 * A concrete implementation of [Job]. It is optionally a child to a parent job.
166 * This job is cancelled when the parent is complete, but not vise-versa.
167 *
168 * This is an open class designed for extension by more specific classes that might augment the
169 * state and mare store addition state information for completed jobs, like their result values.
170 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300171internal open class JobSupport : AbstractCoroutineContextElement(Job), Job {
Roman Elizarovdaa79222017-01-26 11:31:31 +0300172 /*
173 === States ===
174 name state class is Active?
175 ------ ------------ ---------
176 EMPTY Empty : Active -- no completion listener
177 SINGLE JobNode : Active -- a single completion listener
178 SINGLE+ JobNode : Active -- a single completion listener + NodeList added as its next
179 LIST NodeList : Active -- a list of listeners (promoted just once, does not got back to JobNode/Empty)
180 FINAL_C Cancelled : !Active -- cancelled (final state)
181 FINAL_F Failed : !Active -- failed for other reason (final state)
182 FINAL_R <any> : !Active -- produced some result
183
184 === Transitions ===
185
186 Active states !Active states
187 +---------+ +----------+
188 initial -+-> | EMPTY | ------------> | FINAL_* |
189 | +---------+ +----------+
190 | | ^ ^
191 | V | |
192 | +---------+ |
193 | | SINGLE | --------------------+
194 | +---------+ |
195 | | |
196 | V |
197 | +---------+ |
198 +-- | SINGLE+ | --------------------+
199 +---------+ |
200 | |
201 V |
202 +---------+ |
203 | LIST | --------------------+
204 +---------+
205 */
206
Roman Elizarov3754f952017-01-18 20:47:54 +0300207 @Volatile
Roman Elizarovdaa79222017-01-26 11:31:31 +0300208 private var state: Any? = Empty // shared object while we have no listeners
Roman Elizarov3754f952017-01-18 20:47:54 +0300209
Roman Elizarov58a7add2017-01-20 12:19:52 +0300210 @Volatile
211 private var registration: Job.Registration? = null
Roman Elizarov3754f952017-01-18 20:47:54 +0300212
213 protected companion object {
214 @JvmStatic
215 private val STATE: AtomicReferenceFieldUpdater<JobSupport, Any?> =
216 AtomicReferenceFieldUpdater.newUpdater(JobSupport::class.java, Any::class.java, "state")
217 }
218
Roman Elizarovdaa79222017-01-26 11:31:31 +0300219 /**
220 * Initializes parent job.
221 * It shall be invoked at most once after construction after all other initialization.
222 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300223 fun initParentJob(parent: Job?) {
Roman Elizarov58a7add2017-01-20 12:19:52 +0300224 check(registration == null)
Roman Elizarov01934df2017-01-31 09:18:34 +0300225 if (parent == null) {
226 registration = EmptyRegistration
227 return
228 }
Roman Elizarov58a7add2017-01-20 12:19:52 +0300229 // directly pass HandlerNode to parent scope to optimize one closure object (see makeNode)
230 val newRegistration = parent.onCompletion(CancelOnCompletion(parent, this))
231 registration = newRegistration
232 // now check our state _after_ registering (see updateState order of actions)
233 if (state !is Active) newRegistration.unregister()
234 }
235
Roman Elizarovdaa79222017-01-26 11:31:31 +0300236 /**
237 * Returns current state of this job.
238 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300239 fun getState(): Any? = state
Roman Elizarov3754f952017-01-18 20:47:54 +0300240
Roman Elizarovdaa79222017-01-26 11:31:31 +0300241 /**
242 * Tries to update current [state][getState] of this job.
243 */
Roman Elizarov7b2d8b02017-02-02 20:09:14 +0300244 fun updateState(expect: Any, update: Any?): Boolean {
245 if (!tryUpdateState(expect, update)) return false
246 completeUpdateState(expect, update)
247 return true
248 }
249
250 fun tryUpdateState(expect: Any, update: Any?): Boolean {
Roman Elizarovdaa79222017-01-26 11:31:31 +0300251 require(expect is Active && update !is Active) // only active -> inactive transition is allowed
Roman Elizarov3754f952017-01-18 20:47:54 +0300252 if (!STATE.compareAndSet(this, expect, update)) return false
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300253 // #1. Update linked state before invoking completion handlers
254 onStateUpdate(update)
255 // #2. Unregister from parent job
Roman Elizarov58a7add2017-01-20 12:19:52 +0300256 registration?.unregister() // volatile read registration _after_ state was updated
Roman Elizarov7b2d8b02017-02-02 20:09:14 +0300257 return true // continues in completeUpdateState
258 }
259
260 fun completeUpdateState(expect: Any, update: Any?) {
261 // #3. Invoke completion handlers
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300262 val cause = (update as? CompletedExceptionally)?.exception
Roman Elizarov53a0a402017-01-19 20:21:57 +0300263 var completionException: Throwable? = null
Roman Elizarovdaa79222017-01-26 11:31:31 +0300264 when (expect) {
265 // SINGLE/SINGLE+ state -- one completion handler (common case)
266 is JobNode -> try {
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300267 expect.invoke(cause)
Roman Elizarov3754f952017-01-18 20:47:54 +0300268 } catch (ex: Throwable) {
Roman Elizarovdaa79222017-01-26 11:31:31 +0300269 completionException = ex
Roman Elizarov3754f952017-01-18 20:47:54 +0300270 }
Roman Elizarovdaa79222017-01-26 11:31:31 +0300271 // LIST state -- a list of completion handlers
272 is NodeList -> expect.forEach<JobNode> { node ->
273 try {
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300274 node.invoke(cause)
Roman Elizarovdaa79222017-01-26 11:31:31 +0300275 } catch (ex: Throwable) {
276 completionException?.apply { addSuppressed(ex) } ?: run { completionException = ex }
277 }
278
279 }
280 // otherwise -- do nothing (Empty)
Roman Elizarovb6b01252017-02-06 13:17:40 +0300281 else -> check(expect === Empty)
Roman Elizarov3754f952017-01-18 20:47:54 +0300282 }
Roman Elizarov7b2d8b02017-02-02 20:09:14 +0300283 // #4. Do other (overridable) processing after completion handlers
Roman Elizarov53a0a402017-01-19 20:21:57 +0300284 completionException?.let { handleCompletionException(it) }
285 afterCompletion(update)
Roman Elizarov3754f952017-01-18 20:47:54 +0300286 }
287
Roman Elizarov55888f22017-01-26 11:48:37 +0300288 final override val isActive: Boolean get() = state is Active
Roman Elizarov3754f952017-01-18 20:47:54 +0300289
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300290 override fun getCompletionException(): Throwable {
Roman Elizarov7cf452e2017-01-29 21:58:33 +0300291 val state = getState()
292 return when (state) {
293 is Active -> throw IllegalStateException("Job is still active")
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300294 is CompletedExceptionally -> state.exception
295 else -> CancellationException("Job has completed normally")
Roman Elizarov7cf452e2017-01-29 21:58:33 +0300296 }
297 }
298
Roman Elizarov55888f22017-01-26 11:48:37 +0300299 final override fun onCompletion(handler: CompletionHandler): Job.Registration {
Roman Elizarov3754f952017-01-18 20:47:54 +0300300 var nodeCache: JobNode? = null
301 while (true) { // lock-free loop on state
302 val state = this.state
Roman Elizarovdaa79222017-01-26 11:31:31 +0300303 when {
304 // EMPTY state -- no completion handlers
305 state === Empty -> {
306 // try move to SINGLE state
307 val node = nodeCache ?: makeNode(handler).also { nodeCache = it }
308 if (STATE.compareAndSet(this, state, node)) return node
309 }
310 // SINGLE/SINGLE+ state -- one completion handler
311 state is JobNode -> {
312 // try promote it to the list (SINGLE+ state)
Roman Elizarov01934df2017-01-31 09:18:34 +0300313 state.addFirstIfEmpty(NodeList())
Roman Elizarovdaa79222017-01-26 11:31:31 +0300314 // it must be in SINGLE+ state or state has changed (node could have need removed from state)
315 val list = state.next() // either NodeList or somebody else won the race, updated state
316 // just attempt converting it to list if state is still the same, then continue lock-free loop
317 STATE.compareAndSet(this, state, list)
318 }
319 // LIST -- a list of completion handlers
320 state is NodeList -> {
321 val node = nodeCache ?: makeNode(handler).also { nodeCache = it }
Roman Elizarovb6b01252017-02-06 13:17:40 +0300322 if (state.addLastIf(node) { this.state === state }) return node
Roman Elizarovdaa79222017-01-26 11:31:31 +0300323 }
324 // is not active anymore
325 else -> {
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300326 handler((state as? CompletedExceptionally)?.exception)
Roman Elizarovdaa79222017-01-26 11:31:31 +0300327 return EmptyRegistration
328 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300329 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300330 }
331 }
332
Roman Elizarov55888f22017-01-26 11:48:37 +0300333 fun removeNode(node: JobNode) {
Roman Elizarovdaa79222017-01-26 11:31:31 +0300334 // remove logic depends on the state of the job
335 while (true) { // lock-free loop on job state
336 val state = this.state
337 when {
338 // EMPTY state -- no completion handlers
339 state === Empty -> return
340 // SINGE/SINGLE+ state -- one completion handler
341 state is JobNode -> {
342 if (state !== this) return // a different job node --> we were already removed
343 // try remove and revert back to empty state
344 if (STATE.compareAndSet(this, state, Empty)) return
345 }
346 // LIST -- a list of completion handlers
347 state is NodeList -> {
348 // remove node from the list
349 node.remove()
350 return
351 }
352 // is not active anymore
353 else -> return
354 }
355 }
356 }
357
Roman Elizarovf6fed2a2017-02-03 19:12:09 +0300358 final override fun cancel(cause: Throwable?): Boolean {
Roman Elizarov3754f952017-01-18 20:47:54 +0300359 while (true) { // lock-free loop on state
360 val state = this.state as? Active ?: return false // quit if not active anymore
Roman Elizarovf6fed2a2017-02-03 19:12:09 +0300361 if (updateState(state, Cancelled(cause))) return true
Roman Elizarov3754f952017-01-18 20:47:54 +0300362 }
363 }
364
Roman Elizarov53a0a402017-01-19 20:21:57 +0300365 /**
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300366 * Override to make linked state changes before completion handlers are invoked.
367 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300368 open fun onStateUpdate(update: Any?) {}
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300369
370 /**
Roman Elizarov53a0a402017-01-19 20:21:57 +0300371 * Override to process any exceptions that were encountered while invoking [onCompletion] handlers.
372 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300373 open fun handleCompletionException(closeException: Throwable) {
Roman Elizarov53a0a402017-01-19 20:21:57 +0300374 throw closeException
Roman Elizarov3754f952017-01-18 20:47:54 +0300375 }
376
Roman Elizarov53a0a402017-01-19 20:21:57 +0300377 /**
378 * Override for post-completion actions that need to do something with the state.
379 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300380 open fun afterCompletion(state: Any?) {}
Roman Elizarov53a0a402017-01-19 20:21:57 +0300381
Roman Elizarov3754f952017-01-18 20:47:54 +0300382 private fun makeNode(handler: CompletionHandler): JobNode =
383 (handler as? JobNode)?.also { require(it.job === this) }
384 ?: InvokeOnCompletion(this, handler)
385
Roman Elizarov2f6d7c92017-02-03 15:16:07 +0300386 // for nicer debugging
387 override fun toString(): String = "${javaClass.simpleName}{isActive=$isActive}"
388
Roman Elizarovdaa79222017-01-26 11:31:31 +0300389 /**
390 * Marker interface for active [state][getState] of a job.
391 */
Roman Elizarov55888f22017-01-26 11:48:37 +0300392 internal interface Active
Roman Elizarova5676592017-01-19 10:31:13 +0300393
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300394 private object Empty : Active {
395 override fun toString(): String = "Empty"
396 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300397
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300398 private class NodeList : LockFreeLinkedListHead(), Active {
399 override fun toString(): String = buildString {
400 append("[")
401 var first = true
402 this@NodeList.forEach<JobNode> { node ->
403 if (first) first = false else append(", ")
404 append(node)
405 }
406 append("]")
407 }
408 }
Roman Elizarovdaa79222017-01-26 11:31:31 +0300409
410 /**
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300411 * Class for a [state][getState] of a job that had completed exceptionally, including cancellation.
Roman Elizarovdaa79222017-01-26 11:31:31 +0300412 */
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300413 internal open class CompletedExceptionally(cause: Throwable?) {
Roman Elizarov7cf452e2017-01-29 21:58:33 +0300414 @Volatile
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300415 private var _exception: Throwable? = cause // materialize CancellationException on first need
Roman Elizarov7cf452e2017-01-29 21:58:33 +0300416
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300417 val exception: Throwable get() =
418 _exception ?: // atomic read volatile var or else create new
419 CancellationException("Job was cancelled").also { _exception = it }
420
421 override fun toString(): String = "${javaClass.simpleName}[$exception]"
Roman Elizarov3754f952017-01-18 20:47:54 +0300422 }
423
Roman Elizarovdaa79222017-01-26 11:31:31 +0300424 /**
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300425 * A specific subclass of [CompletedExceptionally] for cancelled jobs.
Roman Elizarovdaa79222017-01-26 11:31:31 +0300426 */
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300427 internal class Cancelled(cause: Throwable?) : CompletedExceptionally(cause)
Roman Elizarov3754f952017-01-18 20:47:54 +0300428}
429
430internal abstract class JobNode(
431 val job: Job
Roman Elizarovdaa79222017-01-26 11:31:31 +0300432) : LockFreeLinkedListNode(), Job.Registration, CompletionHandler, JobSupport.Active {
433 // if unregister is called on this instance, then Job was an instance of JobSupport that added this node it itself
434 // directly without wrapping
435 final override fun unregister() = (job as JobSupport).removeNode(this)
Roman Elizarov3754f952017-01-18 20:47:54 +0300436 override abstract fun invoke(reason: Throwable?)
437}
438
439private class InvokeOnCompletion(
440 job: Job,
441 val handler: CompletionHandler
442) : JobNode(job) {
443 override fun invoke(reason: Throwable?) = handler.invoke(reason)
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300444 override fun toString() = "InvokeOnCompletion[${handler.javaClass.name}@${Integer.toHexString(System.identityHashCode(handler))}]"
Roman Elizarov3754f952017-01-18 20:47:54 +0300445}
446
447private class ResumeOnCompletion(
448 job: Job,
449 val continuation: Continuation<Unit>
450) : JobNode(job) {
451 override fun invoke(reason: Throwable?) = continuation.resume(Unit)
452 override fun toString() = "ResumeOnCompletion[$continuation]"
453}
454
455private class UnregisterOnCompletion(
456 job: Job,
457 val registration: Job.Registration
458) : JobNode(job) {
459 override fun invoke(reason: Throwable?) = registration.unregister()
460 override fun toString(): String = "UnregisterOnCompletion[$registration]"
461}
462
463private class CancelOnCompletion(
464 parentJob: Job,
465 val subordinateJob: Job
466) : JobNode(parentJob) {
467 override fun invoke(reason: Throwable?) { subordinateJob.cancel(reason) }
468 override fun toString(): String = "CancelOnCompletion[$subordinateJob]"
469}
470
Roman Elizarov01934df2017-01-31 09:18:34 +0300471internal object EmptyRegistration : Job.Registration {
Roman Elizarov3754f952017-01-18 20:47:54 +0300472 override fun unregister() {}
473 override fun toString(): String = "EmptyRegistration"
474}
475
476private class CancelFutureOnCompletion(
477 job: Job,
478 val future: Future<*>
479) : JobNode(job) {
Roman Elizarova8331812017-01-24 10:04:31 +0300480 override fun invoke(reason: Throwable?) {
481 // Don't interrupt when cancelling future on completion, because no one is going to reset this
482 // interruption flag and it will cause spurious failures elsewhere
483 future.cancel(false)
484 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300485 override fun toString() = "CancelFutureOnCompletion[$future]"
486}
Roman Elizarov53a0a402017-01-19 20:21:57 +0300487
488private class RemoveOnCompletion(
489 job: Job,
490 val node: LockFreeLinkedListNode
491) : JobNode(job) {
492 override fun invoke(reason: Throwable?) { node.remove() }
493 override fun toString() = "RemoveOnCompletion[$node]"
494}
Roman Elizarov58a7add2017-01-20 12:19:52 +0300495
496private class JobImpl(parent: Job? = null) : JobSupport() {
497 init { initParentJob(parent) }
498}