Skip to content

Commit

Permalink
Simplify code around JobSupport (Kotlin#4079)
Browse files Browse the repository at this point in the history
This change simplifies some code used in for `JobSupport`:
there are fewer wrapper classes, the information is passed
between functions with less duplication, and some API was either
removed due to being unused, or moved to reduce its visibility.
  • Loading branch information
dkhalanskyjb authored Apr 9, 2024
1 parent 09ddc06 commit 65ef6ea
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 238 deletions.
2 changes: 2 additions & 0 deletions kotlinx-coroutines-core/common/src/Await.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,7 @@ private class AwaitAll<T>(private val deferreds: Array<out Deferred<T>>) {
// Note that all deferreds are complete here, so we don't need to dispose their nodes
}
}

override val onCancelling = false
}
}
18 changes: 4 additions & 14 deletions kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,6 @@ internal open class CancellableContinuationImpl<in T>(
}
}

private fun callCancelHandler(handler: InternalCompletionHandler, cause: Throwable?) =
/*
* :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
* because we play type tricks on Kotlin/JS and handler is not necessarily a function there
*/
callCancelHandlerSafely { handler.invoke(cause) }

fun callCancelHandler(handler: CancelHandler, cause: Throwable?) =
callCancelHandlerSafely { handler.invoke(cause) }

Expand Down Expand Up @@ -341,10 +334,7 @@ internal open class CancellableContinuationImpl<in T>(
private fun installParentHandle(): DisposableHandle? {
val parent = context[Job] ?: return null // don't do anything without a parent
// Install the handle
val handle = parent.invokeOnCompletion(
onCancelling = true,
handler = ChildContinuation(this)
)
val handle = parent.invokeOnCompletion(handler = ChildContinuation(this))
_parentHandle.compareAndSet(null, handle)
return handle
}
Expand Down Expand Up @@ -627,8 +617,6 @@ private object Active : NotCompleted {
* as seen from the debugger.
* Use [UserSupplied] to create an instance from a lambda.
* We can't avoid defining a separate type, because on JS, you can't inherit from a function type.
*
* @see InternalCompletionHandler for a very similar interface, but used for handling completion and not cancellation.
*/
internal interface CancelHandler : NotCompleted {
/**
Expand Down Expand Up @@ -682,8 +670,10 @@ private data class CompletedContinuation(
// Same as ChildHandleNode, but for cancellable continuation
private class ChildContinuation(
@JvmField val child: CancellableContinuationImpl<*>
) : JobCancellingNode() {
) : JobNode() {
override fun invoke(cause: Throwable?) {
child.parentCancelled(child.getContinuationCancellationCause(job))
}

override val onCancelling = true
}
44 changes: 0 additions & 44 deletions kotlinx-coroutines-core/common/src/CompletionHandler.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,47 +25,3 @@ package kotlinx.coroutines
*/
// TODO: deprecate. This doesn't seem better than a simple function type.
public typealias CompletionHandler = (cause: Throwable?) -> Unit

/**
* Essentially the same as just a function from `Throwable?` to `Unit`.
* The only thing implementors can do is call [invoke].
* The reason this abstraction exists is to allow providing a readable [toString] in the list of completion handlers
* as seen from the debugger.
* Use [UserSupplied] to create an instance from a lambda.
* We can't avoid defining a separate type, because on JS, you can't inherit from a function type.
*
* @see CancelHandler for a very similar interface, but used for handling cancellation and not completion.
*/
internal interface InternalCompletionHandler {
/**
* Signals completion.
*
* This function:
* - Does not throw any exceptions.
* For [Job] instances that are coroutines, exceptions thrown by this function will be caught, wrapped into
* [CompletionHandlerException], and passed to [handleCoroutineException], but for those that are not coroutines,
* they will just be rethrown, potentially crashing unrelated code.
* - Is fast, non-blocking, and thread-safe.
* - Can be invoked concurrently with the surrounding code.
* - Can be invoked from any context.
*
* The meaning of `cause` that is passed to the handler is:
* - It is `null` if the job has completed normally.
* - It is an instance of [CancellationException] if the job was cancelled _normally_.
* **It should not be treated as an error**. In particular, it should not be reported to error logs.
* - Otherwise, the job had _failed_.
*/
fun invoke(cause: Throwable?)

/**
* A lambda passed from outside the coroutine machinery.
*
* See the requirements for [InternalCompletionHandler.invoke] when implementing this function.
*/
class UserSupplied(private val handler: (cause: Throwable?) -> Unit) : InternalCompletionHandler {
/** @suppress */
override fun invoke(cause: Throwable?) { handler(cause) }

override fun toString() = "InternalCompletionHandler.UserSupplied[${handler.classSimpleName}@$hexAddress]"
}
}
2 changes: 1 addition & 1 deletion kotlinx-coroutines-core/common/src/Exceptions.common.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kotlinx.coroutines

/**
* This exception gets thrown if an exception is caught while processing [InternalCompletionHandler] invocation for [Job].
* This exception gets thrown if an exception is caught while processing [CompletionHandler] invocation for [Job].
*
* @suppress **This an internal API and should not be used from general code.**
*/
Expand Down
19 changes: 11 additions & 8 deletions kotlinx-coroutines-core/common/src/Job.kt
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,6 @@ public interface Job : CoroutineContext.Element {
* If the handler would have been invoked earlier if it was registered at that time, then it is invoked immediately,
* unless [invokeImmediately] is set to `false`.
*
* The handler is scheduled to be invoked once the job is cancelled or is complete.
* This behavior can be changed by setting the [onCancelling] parameter to `true`.
* In this case, the handler is invoked as soon as the job becomes _cancelling_ instead.
*
* The meaning of `cause` that is passed to the handler is:
* - It is `null` if the job has completed normally.
* - It is an instance of [CancellationException] if the job was cancelled _normally_.
Expand All @@ -356,12 +352,11 @@ public interface Job : CoroutineContext.Element {
* all the handlers are released when this job completes.
*/
internal fun Job.invokeOnCompletion(
onCancelling: Boolean = false,
invokeImmediately: Boolean = true,
handler: InternalCompletionHandler
handler: JobNode,
): DisposableHandle = when (this) {
is JobSupport -> invokeOnCompletionInternal(onCancelling, invokeImmediately, handler)
else -> invokeOnCompletion(onCancelling, invokeImmediately, handler::invoke)
is JobSupport -> invokeOnCompletionInternal(invokeImmediately, handler)
else -> invokeOnCompletion(handler.onCancelling, invokeImmediately, handler::invoke)
}

/**
Expand Down Expand Up @@ -672,3 +667,11 @@ public object NonDisposableHandle : DisposableHandle, ChildHandle {
*/
override fun toString(): String = "NonDisposableHandle"
}

private class DisposeOnCompletion(
private val handle: DisposableHandle
) : JobNode() {
override fun invoke(cause: Throwable?) = handle.dispose()

override val onCancelling = false
}
Loading

0 comments on commit 65ef6ea

Please sign in to comment.