Coroutines - read from input stream

I am reading buffered input stream inside suspended function.

suspend fun read(): Double? {
        val line =  serialPortReader.readLine()
...

Idea shows warning:
Inappropriate blocking call.

Why is it happening?

readLine is a blocking function.

1 Like

The warning is a signal that your suspend method is doing stuff that blocks instead of suspends.

Blocking calls don’t allow the dispatcher to work on other stuff with the blocked thread so this can hang up you app if a Dispatcher is clogged with blocking calls.

The suspend keyword does not change readLine from a blocking call to a suspending call, if that’s what you were expecting. Your suspend method doesn’t suspend at all. It needs to call a suspend method to do that.

If you want to call a blocking method from coroutines, then be very careful. I recommend using Dispatchers.IO or create your own Dispatcher with a dedicated thread pool.

1 Like

So how should the call look like?
Should I just call asyn{} and read the stream inside?

Change dispatcher to IO, it is intended exactly for wrapping thread suspending calls:

suspend fun OutputStream.readLineSuspending() =
    withContext(Dispatchers.IO) { readLine() }

The compiler warns you because you are making a thread blocking call in a context where you should not block any thread at all, instead you should block the single fiber!

Have a look at the documentation: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html

4 Likes

You can’t simply make a non-blocking call out of blocking call. Like @lamba92 said, you need to use a dispatcher with non-finite number of threads like IO. Or you can try to find a non-blocking implementation and wrap it with coroutines. But it is much more complicated.

ok
so the actual warning here is that at that point kotlin does not know if I am blocking current thread or actually execute this code in IO thread. so If in this function I explicitly specify dispatcher it should resolve warning?

thank you, this is what I’ve been looking for

Well not exactly. When using coroutines you are using a machinery that is one layer of abstraction above theads. As such, threads are a now a resource controlled by that machinery and you should not mess with them for no reason at all. Only the IO dispatcher (unless you write another one) allows to suspend threads.

Each thread-related function has a counterpart in the world of coroutines. You should just learn which one correspond to which.

When using coroutines, tho, you will realize that many common problems of async computation can now be dealt with with very few lines and imperatively. As a direct consequence of that, the needs for threads themselves disappear pretty soon in the coroutines learning process! :slight_smile:

I got the (new) warning too when using Dispatchers.Default with a function that throws IOException. The inspection seem to be checking @Throws(IOException::class) on called function to issue warning Inappropriate blocking method call.

A fix was changing Dispatchers.Default to Dispatchers.IO.
However I considered the Default dispatcher suitable for “long running operations”, like reading from a file may be.

I wonder how internally is IO dispatcher different from Default dispatcher, so that IDE shows this warning. I assume both these dispatchers work on a thread pool, and they should not care if called function blocks due to reading from file, or due to some long operation like sorting huge list.

Default uses a thread pool with size based on CPU count so multiple concurrent blocking operations can block all the threads and block other coroutines from running. IO has an unbounded thread pool, so if all threads are blocked, it’ll create a new one and no coroutine is ever blocked from running.

1 Like