Repository: Kotlin/kotlinx.coroutines
File: kotlinx-coroutines-core/common/src/Job.kt (Kotlin)

Code before:
 * 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_.
 *   **It should not be treated as an error**. In particular, it should not be reported to error logs.
 * - Otherwise, the job had _failed_.
 *
 * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of the registration of this
 * handler and release its memory if its invocation is no longer needed.
 * There is no need to dispose of the handler after completion of this job. The references to
 * all the handlers are released when this job completes.
 */
internal fun Job.invokeOnCompletion(
    onCancelling: Boolean = false,
    invokeImmediately: Boolean = true,
    handler: InternalCompletionHandler
): DisposableHandle = when (this) {
    is JobSupport -> invokeOnCompletionInternal(onCancelling, invokeImmediately, handler)
    is NonCancellable -> NonDisposableHandle
    else -> throw UnsupportedOperationException("third-party implementation of Job is not supported")
}

/**
 * Creates a job object in an active state.
 * A failure of any child of this job immediately causes this job to fail, too, and cancels the rest of its children.
 *
 * To handle children failure independently of each other use [SupervisorJob].
 *
 * If [parent] job is specified, then this job becomes a child job of its parent and
 * is cancelled when its parent fails or is cancelled. All this job's children are cancelled in this case, too.
 *
 * Conceptually, the resulting job works in the same way as the job created by the `launch { body }` invocation
 * (see [launch]), but without any code in the body. It is active until cancelled or completed. Invocation of
 * [CompletableJob.complete] or [CompletableJob.completeExceptionally] corresponds to the successful or
 * failed completion of the body of the coroutine.
 *
 * @param parent an optional parent job.
 */
@Suppress("FunctionName")
public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)

/** @suppress Binary compatibility only */
@Suppress("FunctionName")
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
@JvmName("Job")

Review comment (bug):
This one might reasonably fail.
Could you please see if the pattern from https://github.com/Kotlin/kotlinx.coroutines/issues/2423 keeps working?

Code after:
 * 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_.
 *   **It should not be treated as an error**. In particular, it should not be reported to error logs.
 * - Otherwise, the job had _failed_.
 *
 * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of the registration of this
 * handler and release its memory if its invocation is no longer needed.
 * There is no need to dispose of the handler after completion of this job. The references to
 * all the handlers are released when this job completes.
 */
internal fun Job.invokeOnCompletion(
    onCancelling: Boolean = false,
    invokeImmediately: Boolean = true,
    handler: InternalCompletionHandler
): DisposableHandle = when (this) {
    is JobSupport -> invokeOnCompletionInternal(onCancelling, invokeImmediately, handler)
    else -> invokeOnCompletion(onCancelling, invokeImmediately, handler::invoke)
}

/**
 * Creates a job object in an active state.
 * A failure of any child of this job immediately causes this job to fail, too, and cancels the rest of its children.
 *
 * To handle children failure independently of each other use [SupervisorJob].
 *
 * If [parent] job is specified, then this job becomes a child job of its parent and
 * is cancelled when its parent fails or is cancelled. All this job's children are cancelled in this case, too.
 *
 * Conceptually, the resulting job works in the same way as the job created by the `launch { body }` invocation
 * (see [launch]), but without any code in the body. It is active until cancelled or completed. Invocation of
 * [CompletableJob.complete] or [CompletableJob.completeExceptionally] corresponds to the successful or
 * failed completion of the body of the coroutine.
 *
 * @param parent an optional parent job.
 */
@Suppress("FunctionName")
public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)

/** @suppress Binary compatibility only */
@Suppress("FunctionName")
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
@JvmName("Job")
public fun Job0(parent: Job? = null): Job = Job(parent)