Calling suspending function from regular function


#1

Mixing synchronous and asynchronous code is bad, ok.
Calling blocking code from coroutine is really bad, ok.

Unfortunately Kotlin allow using regular function inside suspending function, we should avoid this?
Probably not, for compatibilty reason with Java.
We should handle this?
Something like:

suspend fun foo(inputStream: InputStream) {
...
val data = run(IO) { inputStream.read() }
...
}

But this rule should be enforced for all Java invocations.

On other side regular function cannot call coroutines, now.
But calling coroutines from regular functions isn’t so bad as above.
So blocking code could call coroutines in blocking mode, ie:

import kotlinx.coroutines.experimental.sync.Mutex
fun baz(mutex: Mutex) {
mutex.lock()
...
mutex.unlock()
}

This default allows better integration between regular function and coroutines.


#2

If you want to block the calling thread while the suspending function is suspended, you can use runBlocking { } coroutine builder.

I believe, no, we shouldn’t. There are a lot of non-suspending functions, and prohibiting to invoke them from suspending functions just because some of them can block seems too restricting.

But this rule should be enforced for all Java invocations.

I really don’t get where does this rule follow from. Does rewriting a blocking function from java to kotlin makes it magically non-blocking?


#3

Most of non-suspend Java function are fast and do not block anything, so they are perfectly safe to use from coroutines. However, there is a subclass of Java (and Kotlin) non-suspend function that block a thread. They need care when being invoked from coroutines if you want to keep your code nicely asynchronous and non-blocking. Identifying those blocking non-suspend functions is a challenge, especially when it comes to IO. It is not Ok, for example, to threat all invocation of InputStream.read as blocking, because the input stream in question can as well be ByteArrayInputStream, which never blocks.

The problem is still important in practice. For example, read carefully Netflix report on converting one of their services to asynchronous code (here). Here is a quote (emphasis is mine):

Other challenges involved converting blocking networking logic into non-blocking networking code, and finding blocking code deep inside libraries, …

So, we should be looking at ways to help with that. The complexity of the problem makes it more amenable to the solution on the level of additional inspections of the code (as opposed to the solution at compiler level) and here is the umbrella issue to keep track and this effort and discuss: https://youtrack.jetbrains.com/issue/KT-15525


#4

No, unfortunately.
all Java function are blocking and (now) I am free to invoke all blocking function within a non blocking code, but this is dangerous becouse blocking a coroutine may involve to block all coroutines.

Here is the point,
regular functions invoke all methods in blocking mode, this is the only option (obviously synchronous invocation doesn’t mean synchronous computation).

Asynchronous functions dsn’t follow this rule, in synchronous function I cannot invoke -directly- suspending function in any mode! I must define a thread pool to invoke a suspending function (using runBLocking in your case).
But if regular functions invoke all method in blocking mode then why I must invoke suspending method in blocking mode explicitly?

Maybe I don’t explain it very well, I am sorry.
My proposal about language design is to allow the invocation of suspending function inside regular function without runBlocking, ie:

mutex.lock()

instead

runBlocking { mutex.lock() }

#5

Hi @elizarov,
I fully agree with you,
but i consider the issue KT-15525 out of the scope of this thread.

My consideration is simply:
why I cannot invoke a suspening function within an regular function without boilerplate?
In such case blocking the thread isn’t a issue: it is the expected behaviour.


#6

Blocking a thread has to be explicit. Otherwise, it is way too easy to make mistake by accidentally omitting suspend modifier on the code you’ve supposed to be async. Right now, if you do this by mistake, the code will not compile. If we make suspend function invocation from non-suspend code “transparent” (with implicit blocking) as you suggest, it is going to be extremely error-prone.


#7

Provide a IO dispatcher for coroutines doesn’t expose to a similar issue?
(see https://github.com/Kotlin/kotlinx.coroutines/issues/79)

run (IO) {
  // blocking code or asynchronous coroutine?
}

A Java old plain Executor doesn’t fit better this use case?
A Callable doesn’t allow coroutine execution, so -as similar case- it is impossible to have an “implicit unblocking”.


#8

Good point. We might need a more explicit name for it. Something like run(BlockingIO) { ... } would look better, but still there is a risk that people will get confused on how it is different from runBlocking { ... }. We’ll need to figure this part of naming.


#9

I think main reason is although context api is nice and short but confusing for new comers and somewhat implicit.
Maybe using rxjava style naming will be better
run(BlockingIO) -> runOn(BlockinIO)
runBlocking -> runOnThisThread (actually after reading couple times through all docs I’m still not sure this is the way runBlocking works since context is implicit and inherited)


#10

Yes,
a nice name can help, but to address the issue might be preferable avoid to start coroutine in blocking mode and use a function like:

suspend fun <T> Executor.invoke(block: /* non suspend */ () -> T): T =
        suspendCoroutine { continuation ->
            execute {
                try {
                    continuation.resume(block())
                } catch(t: Throwable) {
                    continuation.resumeWithException(t)
                }
            }
        }

optionally exposing the CoroutineScope


#11

runOn is interesting idea. I’ve mentioned it in an issue related to naming of dispatchers: https://github.com/Kotlin/kotlinx.coroutines/issues/41


#12

I think that runBlocking is a nice name for running blocking code inside a coroutine.

Following my previous post:

val IOExecutor : Executor = ...
suspend fun <T> runBlocking(executor: Executor = IOExecutor, block: /* non suspend */ () -> T): T = ...

runBlocking { /* no coroutine here */ }