| /* |
| * Copyright 2016-2017 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| @file:JvmMultifileClass |
| @file:JvmName("JobKt") |
| |
| package kotlinx.coroutines.experimental |
| |
| import kotlinx.coroutines.experimental.internalAnnotations.* |
| import kotlinx.coroutines.experimental.selects.* |
| import kotlin.coroutines.experimental.* |
| |
| // --------------- core job interfaces --------------- |
| |
| /** |
| * A background job. Conceptually, a job is a cancellable thing with a simple life-cycle that |
| * culminates in its completion. Jobs can be arranged into parent-child hierarchies where cancellation |
| * or completion of parent immediately cancels all its [children]. |
| * |
| * The most basic instances of [Job] are created with [launch] coroutine builder or with a |
| * `Job()` factory function. Other coroutine builders and primitives like |
| * [Deferred] also implement [Job] interface. |
| * |
| * 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_ (optional transient state) | `true` | `false` | `false` | |
| * | _Cancelling_ (optional transient state) | `false` | `false` | `true` | |
| * | _Cancelled_ (final state) | `false` | `true` | `true` | |
| * | _Completed_ (final state) | `false` | `true` | `false` | |
| * |
| * Usually, a job is created in _active_ state (it is created and started). However, coroutine builders |
| * that provide an optional `start` parameter create a coroutine in _new_ state when this parameter is set to |
| * [CoroutineStart.LAZY]. Such a job can be made _active_ by invoking [start] or [join]. |
| * |
| * A job can be _cancelled_ at any time with [cancel] function that forces it to transition to |
| * _cancelling_ state immediately. Job that is not backed by a coroutine (see `Job()` function) and does not have |
| * [children] becomes _cancelled_ on [cancel] immediately. |
| * Otherwise, job becomes _cancelled_ when it finishes executing its code and |
| * when all its children [complete][isCompleted]. |
| * |
| * ``` |
| * wait children |
| * +-----+ start +--------+ complete +-------------+ finish +-----------+ |
| * | New | ---------------> | Active | -----------> | Completing | -------> | Completed | |
| * +-----+ +--------+ +-------------+ +-----------+ |
| * | | | |
| * | cancel | cancel | cancel |
| * V V | |
| * +-----------+ finish +------------+ | |
| * | Cancelled | <--------- | Cancelling | <----------------+ |
| * |(completed)| +------------+ |
| * +-----------+ |
| * ``` |
| * |
| * A job in the |
| * [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/coroutine-context.html) |
| * represents the coroutine itself. |
| * A job is active while the coroutine is working and job's cancellation aborts the coroutine when |
| * the coroutine is suspended on a _cancellable_ suspension point by throwing [CancellationException]. |
| * |
| * A job can have a _parent_ job. A job with a parent is cancelled when its parent is cancelled or completes exceptionally. |
| * Parent job waits for all its children to complete in _completing_ or _cancelling_ state. |
| * _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. |
| * |
| * 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. |
| */ |
| public interface Job : CoroutineContext.Element { |
| /** |
| * Key for [Job] instance in the coroutine context. |
| */ |
| public companion object Key : CoroutineContext.Key<Job> { |
| /** |
| * Creates a new job object in _active_ state. |
| * It is optionally a child of a [parent] job. |
| * @suppress **Deprecated** |
| */ |
| @Deprecated("Replaced with top-level function", level = DeprecationLevel.HIDDEN) |
| public operator fun invoke(parent: Job? = null): Job = Job(parent) |
| |
| init { |
| /* |
| * Here we make sure that CoroutineExceptionHandler is always initialized in advance, so |
| * that if a coroutine fails due to StackOverflowError we don't fail to report this error |
| * trying to initialize CoroutineExceptionHandler |
| */ |
| CoroutineExceptionHandler |
| } |
| } |
| |
| // ------------ state query ------------ |
| |
| /** |
| * Returns `true` when this job is active -- it was already started and has not completed or cancelled yet. |
| * The job that is waiting for its [children] to complete is still considered to be active if it |
| * was not cancelled. |
| */ |
| public val isActive: Boolean |
| |
| /** |
| * Returns `true` when this job has completed for any reason. A job that was cancelled and has |
| * finished its execution is also considered complete. Job becomes complete only after |
| * all its [children] complete. |
| */ |
| public val isCompleted: Boolean |
| |
| /** |
| * Returns `true` if this job was [cancelled][cancel]. In the general case, it does not imply that the |
| * job has already [completed][isCompleted] (it may still be cancelling whatever it was doing). |
| */ |
| 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 [JobCancellationException] is |
| * returned. The [JobCancellationException.cause] of the resulting [JobCancellationException] references |
| * the original cancellation cause that was passed to [cancel] function. |
| * |
| * This function throws [IllegalStateException] when invoked on a job that has not |
| * [completed][isCompleted] nor [cancelled][isCancelled] yet. |
| */ |
| public fun getCancellationException(): CancellationException |
| |
| /** |
| * @suppress **Deprecated**: Renamed to [getCancellationException] |
| */ |
| @Deprecated("Renamed to getCancellationException", replaceWith = ReplaceWith("getCancellationException()")) |
| public fun getCompletionException(): Throwable = |
| getCancellationException() |
| |
| // ------------ state update ------------ |
| |
| /** |
| * Starts coroutine related to this job (if any) if it was not started yet. |
| * The result `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]. The result is `true` if this job was |
| * cancelled as a result of this invocation and `false` otherwise |
| * (if it was already _completed_ or if it is [NonCancellable]). |
| * Repeated invocations of this function have no effect and always produce `false`. |
| * |
| * When cancellation has a clear reason in the code, an instance of [CancellationException] should be created |
| * at the corresponding original cancellation site and passed into this method to aid in debugging by providing |
| * both the context of cancellation and text description of the reason. |
| */ |
| 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. In particular, this applies to |
| * children created with [launch] coroutine builder. Note, that [async] and other future-like |
| * coroutine builders do not have uncaught exceptions by definition, since all their exceptions are |
| * caught and are encapsulated in their result. |
| */ |
| 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_ state. |
| * |
| * **A child must store the resulting [DisposableHandle] 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 [DisposableHandle] and dispose a handle when they complete. |
| * |
| * @suppress This is an internal API. This method is too error prone for public API. |
| */ |
| @Deprecated(message = "Start child coroutine with 'parent' parameter", level = DeprecationLevel.WARNING) |
| public fun attachChild(child: Job): DisposableHandle |
| |
| /** |
| * Cancels all children jobs of this coroutine with the given [cause]. Unlike [cancel], |
| * the state of this job itself is not affected. |
| * @suppress **Deprecated**: Binary compatibility, it is an extension now |
| */ |
| @Deprecated(message = "Binary compatibility, it is an extension now", level = DeprecationLevel.HIDDEN) |
| public fun cancelChildren(cause: Throwable? = null) |
| |
| // ------------ state waiting ------------ |
| |
| /** |
| * Suspends 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 the cancellation of 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 that was started using |
| * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child |
| * had crashed, unless a non-standard [CoroutineExceptionHandler] if installed in the context. |
| * |
| * This function can be used in [select] invocation with [onJoin] clause. |
| * Use [isCompleted] to check for 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 ------------ |
| |
| /** |
| * @suppress **Deprecated**: For binary compatibility |
| */ |
| @Deprecated(message = "For binary compatibility", level = DeprecationLevel.HIDDEN) |
| public fun invokeOnCompletion(handler: CompletionHandler, onCancelling: Boolean): DisposableHandle |
| |
| /** |
| * @suppress **Deprecated**: Use with named `onCancelling` and `handler` parameters. |
| */ |
| @Deprecated(message = "Use with named `onCancelling` and `handler` parameters", level = DeprecationLevel.WARNING, |
| replaceWith = ReplaceWith("this.invokeOnCompletion(onCancelling = onCancelling_, handler = handler)")) |
| public fun invokeOnCompletion(onCancelling_: Boolean = false, handler: CompletionHandler): DisposableHandle |
| |
| /** |
| * Registers handler that is **synchronously** invoked once on completion of this job. |
| * When job is already complete, then the handler is immediately invoked |
| * with a job's exception or cancellation cause or `null`. Otherwise, handler will be invoked once when this |
| * job is complete. |
| * |
| * 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**: Implementations of `CompletionHandler` must be fast and _lock-free_. |
| */ |
| public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle |
| |
| /** |
| * Registers handler that is **synchronously** invoked once on cancellation or completion of this job. |
| * When job is already cancelling or complete, then the handler is immediately invoked |
| * with a job's cancellation cause or `null` unless [invokeImmediately] is set to false. |
| * Otherwise, handler will be invoked once when this job is cancelled or complete. |
| * |
| * Invocation of this handler on a transition to a transient _cancelling_ state |
| * is controlled by [onCancelling] boolean parameter. |
| * The handler is invoked on invocation of [cancel] when |
| * job becomes _cancelling_ if [onCancelling] parameter is set to `true`. However, |
| * when this [Job] is not backed by a coroutine, like [CompletableDeferred] or [CancellableContinuation] |
| * (both of which do not posses a _cancelling_ state), then the value of [onCancelling] parameter is ignored. |
| * |
| * 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**: This function is a part of internal machinery that supports parent-child hierarchies |
| * and allows for implementation of suspending functions that wait on the Job's state. |
| * This function should not be used in general application code. |
| * Implementations of `CompletionHandler` must be fast and _lock-free_. |
| * |
| * @param onCancelling when `true`, then the [handler] is invoked as soon as this job transitions to _cancelling_ state; |
| * when `false` then the [handler] is invoked only when it transitions to _completed_ state. |
| * @param invokeImmediately when `true` and this job is already in the desired state (depending on [onCancelling]), |
| * then the [handler] is immediately and synchronously invoked and [NonDisposableHandle] is returned; |
| * when `false` then [NonDisposableHandle] is returned, but the [handler] is not invoked. |
| * @param handler the handler. |
| */ |
| 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) = other |
| } |
| |
| /** |
| * Creates a new job object in an _active_ state. |
| * It is optionally a child of a [parent] job. |
| */ |
| @Suppress("FunctionName") |
| public fun Job(parent: Job? = null): Job = JobImpl(parent) |
| |
| /** |
| * A handle to an allocated object that can be disposed to make it eligible for garbage collection. |
| */ |
| public interface DisposableHandle { |
| /** |
| * Disposes the corresponding object, making it eligible for garbage collection. |
| * Repeated invocation of this function has no effect. |
| */ |
| public fun dispose() |
| } |
| |
| // -------------------- Job extensions -------------------- |
| |
| /** |
| * Unregisters a specified [registration] when this job is complete. |
| * |
| * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). |
| * ``` |
| * invokeOnCompletion { registration.unregister() } |
| * ``` |
| * @suppress: **Deprecated**: Renamed to `disposeOnCompletion`. |
| */ |
| @Deprecated(message = "Renamed to `disposeOnCompletion`", |
| replaceWith = ReplaceWith("disposeOnCompletion(registration)")) |
| public fun Job.unregisterOnCompletion(registration: DisposableHandle): DisposableHandle = |
| invokeOnCompletion(handler = DisposeOnCompletion(this, registration).asHandler) |
| |
| /** |
| * 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() } |
| * ``` |
| */ |
| public fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle = |
| invokeOnCompletion(handler = DisposeOnCompletion(this, handle).asHandler) |
| |
| /** |
| * Cancels the job and suspends invoking coroutine until the cancelled job is complete. |
| * |
| * This suspending function is cancellable and **always** checks for the cancellation of 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 that was started using |
| * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child |
| * had crashed, unless a non-standard [CoroutineExceptionHandler] if installed in the context. |
| * |
| * 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 with the given [cause] using [Job.cancel] |
| * for all of them. Unlike [Job.cancel] on this job as a whole, the state of this job itself is not affected. |
| */ |
| @Suppress("EXTENSION_SHADOWED_BY_MEMBER") // See KT-21598 |
| public fun Job.cancelChildren(cause: Throwable? = null) { |
| children.forEach { it.cancel(cause) } |
| } |
| |
| /** |
| * Suspends coroutine until all [children][Job.children] of this job are complete using |
| * [Job.join] for all of them. Unlike [Job.join] on this job as a whole, it does not wait until |
| * this job is complete. |
| */ |
| public suspend fun Job.joinChildren() { |
| children.forEach { it.join() } |
| } |
| |
| // -------------------- CoroutineContext extensions -------------------- |
| |
| /** |
| * Returns `true` when the [Job] of the coroutine in this context is still active |
| * (has not completed and was not cancelled yet). |
| * |
| * 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 `coroutineContext[Job]?.isActive == true`. |
| * See [Job.isActive]. |
| */ |
| public val CoroutineContext.isActive: Boolean |
| get() = this[Job]?.isActive == true |
| |
| /** |
| * Cancels [Job] of this context with an optional cancellation [cause]. The result is `true` if the job was |
| * cancelled as a result of this invocation and `false` if there is no job in the context or if it was already |
| * cancelled or completed. See [Job.cancel] for details. |
| */ |
| public fun CoroutineContext.cancel(cause: Throwable? = null): Boolean = |
| this[Job]?.cancel(cause) ?: false |
| |
| /** |
| * Cancels all children of the [Job] in this context with an optional cancellation [cause]. |
| * It does not do anything if there is no job in the context or it has no children. |
| * See [Job.cancelChildren] for details. |
| */ |
| public fun CoroutineContext.cancelChildren(cause: Throwable? = null) { |
| this[Job]?.cancelChildren(cause) |
| } |
| |
| /** |
| * @suppress **Deprecated**: `join` is now a member function of `Job`. |
| */ |
| @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "DeprecatedCallableAddReplaceWith") |
| @Deprecated(message = "`join` is now a member function of `Job`") |
| public suspend fun Job.join() = this.join() |
| |
| /** |
| * No-op implementation of [DisposableHandle]. |
| */ |
| public object NonDisposableHandle : DisposableHandle { |
| /** Does not do anything. */ |
| override fun dispose() {} |
| |
| /** Returns "NonDisposableHandle" string. */ |
| override fun toString(): String = "NonDisposableHandle" |
| } |