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

Code before:
        // for user-defined handlers it allocates a JobNode object that we might not need, but this is Ok.
        val node: JobNode = makeNode(handler, onCancelling)
        var nodeAdded: JobNode? = null // prevents handler from double invocation
        loopOnState { state ->
            when (state) {
                is Empty -> { // EMPTY_X state -- no completion handlers
                    if (state.isActive) {
                        // try move to SINGLE state
                        if (_state.compareAndSet(state, node)) {
                            nodeAdded = node
                            return node
                        }
                    } else
                        promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
                }
                is Incomplete -> {
                    val list = state.list
                    if (list == null) { // SINGLE/SINGLE+
                        promoteSingleToNodeList(state as JobNode)
                    } else {
                        var rootCause: Throwable? = null
                        var handle: DisposableHandle = NonDisposableHandle
                        if (onCancelling && state is Finishing) {
                            synchronized(state) {
                                // check if we are installing cancellation handler on job that is being cancelled
                                rootCause = state.rootCause // != null if cancelling job
                                // We add node to the list in two cases --- either the job is not being cancelled
                                // or we are adding a child to a coroutine that is not completing yet
                                if (rootCause == null || handler.isHandlerOf<ChildHandleNode>() && !state.isCompleting) {
                                    // Note: add node the list while holding lock on state (make sure it cannot change)
                                    nodeAdded = node
                                    if (!addLast(state, list, node)) {
                                        // parent failed to add ChildCompletion node to the child, the child became Completed
                                        // if ChildCompletion is already invoked, child started parent completion
                                        // return handler, so that parent would wait for the child completion
                                        if (node is ChildCompletion && !node.markInvoked()) return node
                                        return@loopOnState // retry
                                    }
                                    // just return node if we don't have to invoke handler (not cancelling yet)
                                    if (rootCause == null) return node
                                    // otherwise handler is invoked immediately out of the synchronized section & handle returned
                                    handle = node
                                }
                            }
                        }
                        if (rootCause != null) {
                            // Note: attachChild uses invokeImmediately, so it gets invoked when adding to cancelled job
                            if (invokeImmediately) {
                                if (nodeAdded != null) {
                                    nodeAdded!!.invoke(rootCause)
                                } else {

Review comment (suggestion):
[nit] while we are here, please remove `isHandlerOf` function and replace it with `handler is ChildHandleNode`

Code after:
        // for user-defined handlers it allocates a JobNode object that we might not need, but this is Ok.
        val node: JobNode = makeNode(handler, onCancelling)
        /*
         * LinkedList algorithm does not support removing and re-adding the same node,
         * so here we check whether the node is already added to the list to avoid adding the node twice.
         */
        var isNodeAdded = false
        loopOnState { state ->
            when (state) {
                is Empty -> { // EMPTY_X state -- no completion handlers
                    if (state.isActive) {
                        // try move to SINGLE state
                        if (_state.compareAndSet(state, node)) return node
                    } else {
                        promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
                    }
                }
                is Incomplete -> {
                    val list = state.list
                    if (list == null) { // SINGLE/SINGLE+
                        promoteSingleToNodeList(state as JobNode)
                        return@loopOnState // retry
                    }
                    // ...else {, but without nesting
                    var rootCause: Throwable? = null
                    var handle: DisposableHandle = NonDisposableHandle
                    if (onCancelling && state is Finishing) {
                        synchronized(state) {
                            // check if we are installing cancellation handler on job that is being cancelled
                            rootCause = state.rootCause // != null if cancelling job
                            // We add node to the list in two cases --- either the job is not being cancelled
                            // or we are adding a child to a coroutine that is not completing yet
                            if (rootCause == null || handler.isHandlerOf<ChildHandleNode>() && !state.isCompleting) {
                                // Note: add node the list while holding lock on state (make sure it cannot change)
                                if (!isNodeAdded) list.addLast(node)
                                if (this.state !== state) {
                                    /*
                                     * Here we have an additional check for ChildCompletion. Invoking the handler once is not enough
                                     * for this particular kind of node -- the caller makes a decision based on whether the node was added
                                     * or not and that decision should be made **once**.
                                     * To be more precise, the caller of ChildCompletion, in case when it's the last child,
                                     * should make a decision whether to start transition to the final state, based on
                                     * whether the ChildCompletion was added to the list or not. If not -- the JobNode.invoke will do that.
                                     * See comment to JobNode.invoke, we cannot differentiate the situation when external state updater
                                     * invoked or skipped the node, thus we additionally synchronize on 'markInvoked'.
                                     */
                                    if (node is ChildCompletion && !node.markInvoked()) return node
                                    // The state can only change to Complete here, so the node can stay in the list, just retry
                                    return@loopOnState
                                }
                                // just return node if we don't have to invoke handler (not cancelling yet)