Skip to content

Commit 1bb1b49

Browse files
committed
Introduced ChildHandle for attachChild
* ChildHandle.childFailed controls notification from child to parent (no need to lookup parent job in the context) * JobSupport.failsParent boolean property controls whether a job fails its parent on failure.
1 parent 79f4cef commit 1bb1b49

File tree

7 files changed

+63
-40
lines changed

7 files changed

+63
-40
lines changed

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt

+8-5
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public final class kotlinx/coroutines/experimental/CancelledContinuation : kotli
9090
public fun <init> (Lkotlin/coroutines/experimental/Continuation;Ljava/lang/Throwable;)V
9191
}
9292

93+
public abstract interface class kotlinx/coroutines/experimental/ChildHandle : kotlinx/coroutines/experimental/DisposableHandle {
94+
public abstract fun childFailed (Ljava/lang/Throwable;)Z
95+
}
96+
9397
public abstract class kotlinx/coroutines/experimental/CloseableCoroutineDispatcher : kotlinx/coroutines/experimental/CoroutineDispatcher, java/io/Closeable {
9498
public fun <init> ()V
9599
}
@@ -368,11 +372,10 @@ public final class kotlinx/coroutines/experimental/GlobalScope : kotlinx/corouti
368372

369373
public abstract interface class kotlinx/coroutines/experimental/Job : kotlin/coroutines/experimental/CoroutineContext$Element {
370374
public static final field Key Lkotlinx/coroutines/experimental/Job$Key;
371-
public abstract fun attachChild (Lkotlinx/coroutines/experimental/Job;)Lkotlinx/coroutines/experimental/DisposableHandle;
375+
public abstract fun attachChild (Lkotlinx/coroutines/experimental/Job;)Lkotlinx/coroutines/experimental/ChildHandle;
372376
public abstract fun cancel (Ljava/lang/Throwable;)Z
373377
public abstract fun cancelChild (Lkotlinx/coroutines/experimental/Job;)V
374378
public abstract synthetic fun cancelChildren (Ljava/lang/Throwable;)V
375-
public abstract fun childFailed (Ljava/lang/Throwable;)Z
376379
public abstract fun getCancellationException ()Ljava/util/concurrent/CancellationException;
377380
public abstract fun getChildren ()Lkotlin/sequences/Sequence;
378381
public abstract fun getCompletionException ()Ljava/lang/Throwable;
@@ -442,11 +445,10 @@ public final class kotlinx/coroutines/experimental/LazyDeferredKt {
442445

443446
public final class kotlinx/coroutines/experimental/NonCancellable : kotlin/coroutines/experimental/AbstractCoroutineContextElement, kotlinx/coroutines/experimental/Job {
444447
public static final field INSTANCE Lkotlinx/coroutines/experimental/NonCancellable;
445-
public fun attachChild (Lkotlinx/coroutines/experimental/Job;)Lkotlinx/coroutines/experimental/DisposableHandle;
448+
public fun attachChild (Lkotlinx/coroutines/experimental/Job;)Lkotlinx/coroutines/experimental/ChildHandle;
446449
public fun cancel (Ljava/lang/Throwable;)Z
447450
public fun cancelChild (Lkotlinx/coroutines/experimental/Job;)V
448451
public synthetic fun cancelChildren (Ljava/lang/Throwable;)V
449-
public fun childFailed (Ljava/lang/Throwable;)Z
450452
public fun getCancellationException ()Ljava/util/concurrent/CancellationException;
451453
public fun getChildren ()Lkotlin/sequences/Sequence;
452454
public fun getCompletionException ()Ljava/lang/Throwable;
@@ -464,8 +466,9 @@ public final class kotlinx/coroutines/experimental/NonCancellable : kotlin/corou
464466
public fun start ()Z
465467
}
466468

467-
public final class kotlinx/coroutines/experimental/NonDisposableHandle : kotlinx/coroutines/experimental/DisposableHandle {
469+
public final class kotlinx/coroutines/experimental/NonDisposableHandle : kotlinx/coroutines/experimental/ChildHandle, kotlinx/coroutines/experimental/DisposableHandle {
468470
public static final field INSTANCE Lkotlinx/coroutines/experimental/NonDisposableHandle;
471+
public fun childFailed (Ljava/lang/Throwable;)Z
469472
public fun dispose ()V
470473
public fun toString ()Ljava/lang/String;
471474
}

common/kotlinx-coroutines-core-common/src/AbstractCoroutine.kt

-7
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,6 @@ public abstract class AbstractCoroutine<in T>(
123123
makeCompletingOnce(CompletedExceptionally(exception), defaultResumeMode)
124124
}
125125

126-
// todo: make it for all kinds of coroutines, now only launch & actor override and handleExceptionViaJob
127-
internal fun failParentImpl(exception: Throwable): Boolean {
128-
if (exception is CancellationException) return true
129-
val parentJob = parentContext[Job]
130-
return parentJob !== null && parentJob.childFailed(exception)
131-
}
132-
133126
internal final override fun handleOnCompletionException(exception: Throwable) {
134127
handleCoroutineException(parentContext, exception, this)
135128
}

common/kotlinx-coroutines-core-common/src/Builders.common.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ private open class StandaloneCoroutine(
210210
parentContext: CoroutineContext,
211211
active: Boolean
212212
) : AbstractCoroutine<Unit>(parentContext, active) {
213-
override fun failParent(exception: Throwable) = failParentImpl(exception)
213+
override val failsParent: Boolean get() = true
214214
override fun handleJobException(exception: Throwable) = handleExceptionViaHandler(parentContext, exception)
215215
}
216216

common/kotlinx-coroutines-core-common/src/Job.kt

+31-14
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,6 @@ public interface Job : CoroutineContext.Element {
181181

182182
// ------------ parent-child ------------
183183

184-
/**
185-
* Child is reporting failure to the parent by invoking this method.
186-
* This method is invoked by the child twice. The first time child report its root cause as soon as possible,
187-
* so that all its siblings and the parent can start finishing their work asap on failure. The second time
188-
* child invokes this method when it had aggregated and determined its final termination cause.
189-
*
190-
* @suppress **This is unstable API and it is subject to change.**
191-
*/
192-
public fun childFailed(cause: Throwable): Boolean
193-
194184
/**
195185
* Cancels child job. This method is invoked by [parentJob] to cancel this child job.
196186
* Child finds the cancellation cause using [getCancellationException] of the [parentJob].
@@ -240,7 +230,7 @@ public interface Job : CoroutineContext.Element {
240230
* @suppress **This is unstable API and it is subject to change.**
241231
* This is an internal API. This method is too error prone for public API.
242232
*/
243-
public fun attachChild(child: Job): DisposableHandle
233+
public fun attachChild(child: Job): ChildHandle
244234

245235
/**
246236
* Cancels all children jobs of this coroutine with the given [cause]. Unlike [cancel],
@@ -396,6 +386,21 @@ public interface DisposableHandle {
396386
public fun dispose()
397387
}
398388

389+
/**
390+
* @suppress **This is unstable API and it is subject to change.**
391+
*/
392+
public interface ChildHandle : DisposableHandle {
393+
/**
394+
* Child is reporting failure to the parent by invoking this method.
395+
* This method is invoked by the child twice. The first time child report its root cause as soon as possible,
396+
* so that all its siblings and the parent can start finishing their work asap on failure. The second time
397+
* child invokes this method when it had aggregated and determined its final termination cause.
398+
*
399+
* @suppress **This is unstable API and it is subject to change.**
400+
*/
401+
public fun childFailed(cause: Throwable): Boolean
402+
}
403+
399404
// -------------------- Job extensions --------------------
400405

401406
/**
@@ -508,10 +513,22 @@ public suspend fun Job.join() = this.join()
508513
/**
509514
* No-op implementation of [DisposableHandle].
510515
*/
511-
public object NonDisposableHandle : DisposableHandle {
512-
/** Does not do anything. */
516+
public object NonDisposableHandle : DisposableHandle, ChildHandle {
517+
/**
518+
* Does not do anything.
519+
* @suppress
520+
*/
513521
override fun dispose() {}
514522

515-
/** Returns "NonDisposableHandle" string. */
523+
/**
524+
* Returns `false`.
525+
* @suppress
526+
*/
527+
override fun childFailed(cause: Throwable): Boolean = false
528+
529+
/**
530+
* Returns "NonDisposableHandle" string.
531+
* @suppress
532+
*/
516533
override fun toString(): String = "NonDisposableHandle"
517534
}

common/kotlinx-coroutines-core-common/src/JobSupport.kt

+21-8
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
125125
private val _state = atomic<Any?>(if (active) EMPTY_ACTIVE else EMPTY_NEW)
126126

127127
@Volatile
128-
private var parentHandle: DisposableHandle? = null
128+
private var parentHandle: ChildHandle? = null
129129

130130
// ------------ initialization ------------
131131

@@ -222,7 +222,9 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
222222
}
223223
// Now handle exception
224224
if (finalException != null) {
225-
if (!failParent(finalException)) handleJobException(finalException)
225+
if (!failParent(finalException)) {
226+
handleJobException(finalException)
227+
}
226228
}
227229
// Then CAS to completed state -> it must succeed
228230
require(_state.compareAndSet(state, finalState)) { "Unexpected state: ${_state.value}, expected: $state, update: $finalState" }
@@ -614,7 +616,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
614616
fail(cause, cancel = true) && handlesException
615617

616618
// child is reporting failure to the parent
617-
public override fun childFailed(cause: Throwable) =
619+
internal fun childFailed(cause: Throwable) =
618620
fail(cause, cancel = false) && handlesException
619621

620622
// parent is cancelling child
@@ -869,14 +871,14 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
869871
}
870872

871873
@Suppress("OverridingDeprecatedMember")
872-
public final override fun attachChild(child: Job): DisposableHandle {
874+
public final override fun attachChild(child: Job): ChildHandle {
873875
/*
874876
* Note: This function attaches a special ChildNode object. This node object
875877
* is handled in a special way on completion on the coroutine (we wait for all of them) and
876878
* is handled specially by invokeOnCompletion itself -- it adds this node to the list even
877879
* if the job is already failing.
878880
*/
879-
return invokeOnCompletion(onFailing = true, handler = ChildJob(this, child).asHandler)
881+
return invokeOnCompletion(onFailing = true, handler = ChildJob(this, child).asHandler) as ChildHandle
880882
}
881883

882884
@Suppress("OverridingDeprecatedMember")
@@ -901,8 +903,12 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
901903
*/
902904
internal open fun onFailing(cause: Throwable?) {}
903905

904-
// todo: make it for all kinds of coroutines, now only launch & actor override and handleExceptionViaJob
905-
internal open fun failParent(exception: Throwable): Boolean = false
906+
/**
907+
* When this function returns `true` the parent fails on the failure of this job.
908+
*
909+
* @suppress **This is unstable API and it is subject to change.*
910+
*/
911+
protected open val failsParent: Boolean get() = false
906912

907913
/**
908914
* Returns `true` for jobs that handle their exceptions via [handleJobException] or integrate them
@@ -921,6 +927,12 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
921927
*/
922928
protected open fun handleJobException(exception: Throwable) {}
923929

930+
private fun failParent(cause: Throwable): Boolean {
931+
if (cause is CancellationException) return true
932+
if (!failsParent) return false
933+
return parentHandle?.childFailed(cause) == true
934+
}
935+
924936
/**
925937
* Override for post-completion actions that need to do something with the state.
926938
* @param state the final state.
@@ -1280,8 +1292,9 @@ private class InvokeOnFailing(
12801292
internal class ChildJob(
12811293
parent: JobSupport,
12821294
@JvmField val childJob: Job
1283-
) : JobFailingNode<JobSupport>(parent) {
1295+
) : JobFailingNode<JobSupport>(parent), ChildHandle {
12841296
override fun invoke(cause: Throwable?) = childJob.cancelChild(job)
1297+
override fun childFailed(cause: Throwable): Boolean = job.childFailed(cause)
12851298
override fun toString(): String = "ChildJob[$childJob]"
12861299
}
12871300

common/kotlinx-coroutines-core-common/src/NonCancellable.kt

+1-4
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job {
6868
/** Always returns `false`. */
6969
override fun cancel(cause: Throwable?): Boolean = false // never handles exceptions
7070

71-
/** @suppress */
72-
override fun childFailed(cause: Throwable): Boolean = false // never handles exceptions
73-
7471
/** @suppress */
7572
override fun cancelChild(parentJob: Job): Unit = error("Cannot be invoked, does not have a parent")
7673

@@ -80,7 +77,7 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job {
8077

8178
/** Always returns [NonDisposableHandle] and does not do anything. */
8279
@Suppress("OverridingDeprecatedMember")
83-
override fun attachChild(child: Job): DisposableHandle = NonDisposableHandle
80+
override fun attachChild(child: Job): ChildHandle = NonDisposableHandle
8481

8582
/** Does not do anything. */
8683
@Suppress("OverridingDeprecatedMember")

core/kotlinx-coroutines-core/src/channels/Actor.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ private open class ActorCoroutine<E>(
197197
_channel.cancel(cause)
198198
}
199199

200-
override fun failParent(exception: Throwable) = failParentImpl(exception)
200+
override val failsParent: Boolean get() = true
201201
override fun handleJobException(exception: Throwable) = handleExceptionViaHandler(parentContext, exception)
202202
}
203203

0 commit comments

Comments
 (0)