Skip to content

runBlocking doesnt block Main thread on iOS #4345

@slipdef

Description

@slipdef

We used to use our custom implementation to run some important cleaning procedure on applicationWillTerminate event on iOS. The procedure requires suspend func invocation, so we use custom runblocking that uses NSRunLoop.mainRunLoop inside. I noticed that coroutines in latest versions provides own impl of runBlocking for Native. The test reveals that it doesnt block the thread (MainThread) in applicationWillTerminate.

Activity

dkhalanskyjb

dkhalanskyjb commented on Feb 7, 2025

@dkhalanskyjb
Collaborator

Sorry, could you explain what you mean? Could you maybe provide a code example? runBlocking is itself not a suspend function, so it certainly blocks the thread it's running on.

slipdef

slipdef commented on Jun 24, 2025

@slipdef
Author

Sorry, didnt notice the notification. I mean that I have iOS code smth like this

@objc private func applicationWillTerminate() {
           someKotlinMPClass.finalizeCurrentWork()
    }

fun finalizeCurrentWork() {
        runBlocking {
            someSuspendFunc()
        }
    }

Our custom impl of runBlocking for iOS that blocks well (taken from some Kotlin coroutines Github issue):

import kotlinx.coroutines.*
import platform.Foundation.*
import kotlin.concurrent.AtomicReference
import kotlin.coroutines.CoroutineContext

actual fun <T> runBlocking(block: suspend CoroutineScope.() -> T): T {
    val expectation = Expectation<T>()
    val exceptionHolder = AtomicReference<Throwable?>(null)

    GlobalScope.launch(MainRunLoopDispatcher) {
        try {
            expectation.fulfill(block.invoke(this))
        } catch (e: Throwable) {
            exceptionHolder.value = e
        }
    }

    @Suppress("UNCHECKED_CAST")
    return expectation.wait().also {
        exceptionHolder.value?.let { throw it }
    } as T
}

private object MainRunLoopDispatcher : MainCoroutineDispatcher(), Delay {
    override val immediate: MainCoroutineDispatcher
        get() = this

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        NSRunLoop.mainRunLoop().performBlock {
            block.run()
        }
    }

    override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
        (Dispatchers.Main as Delay).scheduleResumeAfterDelay(timeMillis, continuation)
    }
}

private class Expectation<T> {
    private var waiting = true
    private var result: T? = null

    fun fulfill(result: T?) {
        waiting = false
        this.result = result
    }

    fun wait(): T? {
        while (waiting) {
            advanceRunLoop()
        }

        return result
    }
}

private fun advanceRunLoop() {
    val date = NSDate().addTimeInterval(1.0) as NSDate
    NSRunLoop.mainRunLoop.runUntilDate(date)
}
dkhalanskyjb

dkhalanskyjb commented on Jun 25, 2025

@dkhalanskyjb
Collaborator

Sorry, I still don't understand the expected outcome and how what runBlocking is doing is different. Do you mean that you want the runBlocking code to run on some other dispatcher instead of the current thread? Then, you can do runBlocking(myDispatcher) { myCode() }.

slipdef

slipdef commented on Jun 30, 2025

@slipdef
Author

@objc private func applicationWillTerminate() { runBlocking { someSuspendFunc() } }

I mean I want and expect that runBlocking in the code above would block the current thread (UI thread in that case), but it doesn't do it. Does it match you expecations or I missed something?

dkhalanskyjb

dkhalanskyjb commented on Jun 30, 2025

@dkhalanskyjb
Collaborator

it doesn't do it

Here's the thing: I can not imagine how this would be possible. Could you please provide some code showing that runBlocking does not block the thread it runs on?

slipdef

slipdef commented on Jun 30, 2025

@slipdef
Author

Ok, I will try to come up with repro sample on 1.10.2 version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @slipdef@dkhalanskyjb

        Issue actions

          runBlocking doesnt block Main thread on iOS · Issue #4345 · Kotlin/kotlinx.coroutines