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

6 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.

1 Like

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

so if i have understand it correctly, suspend modifier doesn’t add the capability of suspending caller thread just by being added to to function signature.

the newbies think that if they add the suspend modifier to their function and make a Http connection inside the function, it will simply be called on another thread and will suspend the calling thread.

this confusion is because all the examples in conferences and talks use only delay function as a time consuming task, but in the real world the tasks that we wait for mostly are making connections or reading from streams.so we should always wrap our time consuming function inside the withContext function and it will run the function on another thread and will pause the caller thread

the thing that i don’t understand is what is the purpose of suspend modifier when we can simply switch the thread and wait for new thread to complete by using withContext function or like so?

another thing worth mentioning is that just by using Coroutins we don’t magically get the capability of not waiting for http connections to connect or reading from streams.we actually do wait for them, in fact while they are being waited, they are running inside another thread and when they are ready , the coroutine mechanism switches the thread for you, just that.i mentioned that because they describe coroutines like waiting tasks wont take any thread or resources and the coroutines will listen to them

Sounds like you have some misconceptions about what coroutines are. They are not threads. You cannot suspend a thread, you can only block it, or return early so it can do other stuff.

The suspend keyword is just syntactic sugar for regular async callback code. Every suspend method has an extra callback parameter. When you suspend, you are actually returning early with the expectation that a callback with the rest of your method will be invoked later. The thread keeps running, it’s just running other coroutine code.

Kotlin can’t automatically put stuff on the IO dispatcher because it has no way to know at compile time whether a call will actually block or not. It can guess and try to give IDE warnings, but it doesn’t really know. That’s why it’s your responsibility.

Try reading this from the docs: https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html

And if you want a more detailed under the hood explanation: The suspend modifier — Under the hood

they are not threads for sure, but they run on threads.thanks for the article on how it works internally.but i still believe that coroutines are just an abstraction over threads, and they just make it easy to distribute your code on multiple threads.they create and pass callbacks as continuation on our behalf just so that our code become cleaner and look sequential.others when talk about coroutines pretend that it is something other than threads, they like to see the things magically

and i think that they have just added suspend modifier because it was very costly to create and pass continuation callbacks at each suspension points, so they have decided to handle inside suspend functions with state machine