Fixed initialization of Job with parent (initParentJob), fixed handling on uncaught exceptions in standalone coroutines
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
index 9723585..2895b4c 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
@@ -17,11 +17,11 @@
/**
* Suspend coroutine similar to [suspendCoroutine], but provide an implementation of [CancellableContinuation] to
- * the [block].
+ * the [block]. This function throws [CancellationException] if the coroutine is cancelled while suspended.
*/
public inline suspend fun <T> suspendCancellableCoroutine(crossinline block: (CancellableContinuation<T>) -> Unit): T =
- suspendCoroutineOrReturn { c ->
- val safe = SafeCancellableContinuation(c)
+ suspendCoroutineOrReturn { cont ->
+ val safe = SafeCancellableContinuation(cont, getParentJobOrAbort(cont))
block(safe)
safe.getResult()
}
@@ -29,12 +29,23 @@
// --------------- implementation details ---------------
@PublishedApi
+internal fun getParentJobOrAbort(cont: Continuation<*>): Job? {
+ val job = cont.context[Job]
+ // fast path when parent job is already complete (we don't even construct SafeCancellableContinuation object)
+ job?.isActive?.let { if (!it) throw CancellationException() }
+ return job
+}
+
+@PublishedApi
internal class SafeCancellableContinuation<in T>(
- private val delegate: Continuation<T>
+ private val delegate: Continuation<T>,
+ parentJob: Job?
) : AbstractCoroutine<T>(delegate.context), CancellableContinuation<T> {
// only updated from the thread that invoked suspendCancellableCoroutine
private var suspendedThread: Thread? = Thread.currentThread()
+ init { initParentJob(parentJob) }
+
fun getResult(): Any? {
if (suspendedThread != null) {
suspendedThread = null