You would need to make blocking versions of your external API that enters a runBlocking context to call the suspending API. Just don’t call them from a coroutine.
Similarly, you can have asSequence the uses runBlocking as long as you don’t call it from a coroutine. If you need a version to call from a coroutine, it should make a Flow.
I know this seems annoying, but that’s because you haven’t experienced the pain of doing it in Java without using coroutines at all.
Thanks, I will give it another try to see how far I get before I run into another issue.
One conclusion I’m drawing from this disussion is that runBlocking is only a safe function to call if you are in control of the entire application. For example, it would appear that calling it from within a library is unsafe, since you are not in control of the caller, and if your function ends up being called from another runBlocking, you can end up with a deadlock.
Is this is a reasonable interpretation of the situation? And if so, what is a safe way of calling suspending functions from a library that exposes non-suspending API calls (for example something that needs a Java API).
Implementing synchronous library APIs with asynchronous code is dangerous in general. This is not unique to Kotlin or coroutines. Last time I hit this type of problem was C++ with regular callbacks and we ended up ditching the async internals.
If implementing a synchronous API with internals, the library has to ensure that a blocking call will never be made from an event loop used as part of its implementation, even transitively.
For example, the synchronous library API could restrict itself to only use the dispatcher created by runBlocking (it loops synchronously inside runBlocking). A private thread pool could be used too but you’d need to ensure that these blocking library APIs were never called from the thread pool even internally.
It’s easy to mess up which is why it’s not recommended. If you are using coroutines you should really expose suspend methods.