launched jobs and await can be cancelled while waiting in dispatch queue
* suspendAtomicCancellableCoroutine function is introduced for funs like
send/receive/receiveOrNull that require atomic cancellation
(they cannot be cancelled after decision was made)
* Coroutines started with default mode (CoroutineStart.ATOMIC) using
async/launch/actor builders can be cancelled before execution starts.
* CoroutineStart.ATOMIC is introduced as a start mode to specify that
coroutine cannot be cancelled before its execution start.
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt
index 6389d53..01f7ed2 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt
@@ -35,6 +35,12 @@
*
* Note, that [Unconfined] dispatcher always returns `false` from its [CoroutineDispatcher.isDispatchNeeded]
* function, so starting coroutine with [Unconfined] dispatcher by [DEFAULT] is the same as using [UNDISPATCHED].
+ *
+ * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
+ * execution at all, but complete with an exception.
+ *
+ * Cancellability of coroutine at suspension points depends on the particular implementation details of
+ * suspending functions. Use [suspendCancellableCoroutine] to implement cancellable suspending functions.
*/
DEFAULT,
@@ -43,10 +49,22 @@
*
* See the documentation for the corresponding coroutine builders for details:
* [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor].
+ *
+ * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
+ * execution at all, but complete with an exception.
*/
LAZY,
/**
+ * Atomically schedules coroutines for execution according to its context. This is similar to [DEFAULT],
+ * but the coroutine cannot be cancelled before it starts executing.
+ *
+ * Cancellability of coroutine at suspension points depends on the particular implementation details of
+ * suspending functions as in [DEFAULT].
+ */
+ ATOMIC,
+
+ /**
* Immediately executes coroutine until its first suspension point _in the current thread_ as if it the
* coroutine was started using [Unconfined] dispatcher. However, when coroutine is resumed from suspension
* it is dispatched according to the [CoroutineDispatcher] in its context.
@@ -56,13 +74,15 @@
/**
* Starts the corresponding block as a coroutine with this coroutine start strategy.
*
- * * [DEFAULT] uses [startCoroutine].
+ * * [DEFAULT] uses [startCoroutineCancellable].
+ * * [ATOMIC] uses [startCoroutine].
* * [UNDISPATCHED] uses [startCoroutineUndispatched].
* * [LAZY] does nothing.
*/
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>) =
when (this) {
- CoroutineStart.DEFAULT -> block.startCoroutine(receiver, completion)
+ CoroutineStart.DEFAULT -> block.startCoroutineCancellable(receiver, completion)
+ CoroutineStart.ATOMIC -> block.startCoroutine(receiver, completion)
CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
CoroutineStart.LAZY -> Unit // will start lazily
}