Repository: Kotlin/kotlinx.coroutines
File: kotlinx-coroutines-core/native/src/Builders.kt (Kotlin)

Code before:

    /**
     * Send a ping to the worker to prevent it from terminating while this coroutine is running,
     * ensuring that continuations don't get dropped and forgotten.
     */
    fun keepAlive() {
        Worker.current.executeAfter(afterMicroseconds = 100_000) {
            if (!isCompleted) {
                keepAlive()
            }
        }
    }

    override val isScopedCoroutine: Boolean get() = true

    override fun afterCompletion(state: Any?) {
        // wake up blocked thread
        if (joinWorker != Worker.current) {
            // Unpark waiting worker
            joinWorker.executeAfter(0L, {}) // send an empty task to unpark the waiting event loop
        }
    }

    @Suppress("UNCHECKED_CAST")
    fun joinBlocking(): T {
        try {
            eventLoop?.incrementUseCount()
            while (true) {
                var parkNanos: Long
                // Workaround for bug in BE optimizer that cannot eliminate boxing here
                if (eventLoop != null) {
                    parkNanos = eventLoop.processNextEvent()
                } else {
                    parkNanos = Long.MAX_VALUE
                }
                // note: processNextEvent may lose unpark flag, so check if completed before parking
                if (isCompleted) break
                joinWorker.park(parkNanos / 1000L, true)
            }
        } finally { // paranoia
            eventLoop?.decrementUseCount()
        }
        // now return result
        val state = state.unboxState()
        (state as? CompletedExceptionally)?.let { throw it.cause }
        return state as T
    }
}


Review comment (suggestion):
I'm not really fond of this change.
It has really weird semantics "let's invoke something on a regular basis to workaround the issue that only reproduces during termination", it interferes with nested `runBlocking`s (producing probably unnecessary tasks) and opens a can of worms for "what happens if 'start' or 'joinBlocking' throw system exception (e.g. OOM)". For now it seems it will create a task that is just stuck forever

Code after:
@ThreadLocal
private object ThreadLocalKeepAlive {
    /** If any of these checks passes, this means this [Worker] is still used. */
    private var checks = mutableListOf<() -> Boolean>()

    /** Whether the worker currently tries to keep itself alive. */
    private var keepAliveLoopActive = false

    /** Adds another stopgap that must be passed before the [Worker] can be terminated. */
    fun addCheck(terminationForbidden: () -> Boolean) {
        checks.add(terminationForbidden)
        if (!keepAliveLoopActive) keepAlive()
    }

    /**
     * Send a ping to the worker to prevent it from terminating while this coroutine is running,
     * ensuring that continuations don't get dropped and forgotten.
     */
    private fun keepAlive() {
        // only keep the checks that still forbid the termination
        checks = checks.filter { it() }.toMutableList()
        // if there are no checks left, we no longer keep the worker alive, it can be terminated
        keepAliveLoopActive = checks.isNotEmpty()
        if (keepAliveLoopActive) {
            Worker.current.executeAfter(afterMicroseconds = 100_000) {
                keepAlive()
            }
        }
    }
}

private class BlockingCoroutine<T>(
    parentContext: CoroutineContext,
    private val eventLoop: EventLoop?
) : AbstractCoroutine<T>(parentContext, true, true) {
    private val joinWorker = Worker.current

    override val isScopedCoroutine: Boolean get() = true

    override fun afterCompletion(state: Any?) {
        // wake up blocked thread
        if (joinWorker != Worker.current) {
            // Unpark waiting worker
            joinWorker.executeAfter(0L, {}) // send an empty task to unpark the waiting event loop
        }
    }

    @Suppress("UNCHECKED_CAST")
    fun joinBlocking(): T {
        try {
            eventLoop?.incrementUseCount()