Kotlin/kotlinx.coroutines

Provide a Public API to Set a Custom Main Coroutine Dispatcher for Advanced Use Cases

leinardi opened this issue · 0 comments

Use case

I am creating Kotlin/Native bindings for GTK and need to set a custom CoroutineDispatcher as the main dispatcher. This is essential because GTK operates with its own main loop, and for proper coroutine integration, it is critical to dispatch tasks onto GTK's main thread.

In testing, I can achieve this using kotlinx.coroutines.test.setMain, but in production, there is no public API to set a custom main dispatcher. Attempting to use Dispatchers.injectMain() results in a build failure because the function is internal to kotlinx.coroutines.

This limitation hinders the ability to integrate Kotlin/Native projects with custom event loops effectively and safely. While a workaround exists using @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"), it is fragile and relies on internal APIs that could change without notice.

The Shape of the API

The desired API would be a public, stable way to set a custom MainCoroutineDispatcher. A possible shape of the API could be:

Dispatchers.setMain(customDispatcher: MainCoroutineDispatcher)

Example usage:

val gtkDispatcher = GtkDispatcher() // Custom MainCoroutineDispatcher for GTK's main loop
Dispatchers.setMain(gtkDispatcher)

// Use Dispatchers.Main in the rest of the application
launch(Dispatchers.Main) {
    // Code that needs to run on the GTK main thread
}

To ensure that this feature is not used unintentionally or inappropriately, the API should require explicit opt-in via an annotation such as @OptIn(ExperimentalMainDispatcherApi::class). This would make it clear to developers that this functionality is intended only for very specific and advanced use cases, such as custom platforms or event loop integrations, and not for general-purpose use.

Example of the opt-in requirement:

@OptIn(ExperimentalMainDispatcherApi::class)
fun configureMainDispatcher() {
    val gtkDispatcher = GtkDispatcher()
    Dispatchers.setMain(gtkDispatcher)
}

This ensures that the API remains accessible for legitimate use cases but highlights its advanced nature, encouraging developers to consider carefully before employing it. Let me know if there’s anything else you’d like to refine!

Prior Art

  • The kotlinx.coroutines.test.setMain API provides a similar functionality for testing, but it is limited to test environments.

The current reliance on internal APIs makes this integration fragile and hard to maintain. Providing official support for injecting a custom main dispatcher would make kotlinx.coroutines more versatile for frameworks and platforms with custom main loops.