Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Suspended function to read from InputStream

I'm fairly new to coroutines, therefore I wanted to ask for an opinion.

I've created an extension function to read data from the InputStream:

suspend fun InputStream.readData(): ByteArray {
    return withContext(Dispatchers.IO) {
        while (available() == 0) {
            delay(10)
        }
        val count = available()
        val buffer = ByteArray(count)
        read(buffer, 0, count)
        return@withContext buffer
    }
}

Do you think is there something I could improve from the coroutines point of view?

like image 658
Krokodylowy Avatar asked Jan 10 '19 01:01

Krokodylowy


People also ask

How do you use Suspend function in Java?

Java Thread suspend() methodThe suspend() method of thread class puts the thread from running to waiting state. This method is used if you want to stop the thread execution and start it again when a certain event occurs. This method allows a thread to temporarily cease execution.

What is the difference between suspending vs blocking?

Suspended means directly stopped. Blocked means indirectly stopped by something higher priority or intervening in access to some mutually accessible resource.

Are coroutines blocking?

Coroutines are more efficient than threads because they are suspended and resumed instead of blocking execution. However, we need to block threads in some specific use cases. For example, in the main() function, we need to block the thread, otherwise, our program will end without waiting for the coroutines to complete.

What is kotlin InputStream?

An InputStream you can create on a ParcelFileDescriptor, which will take care of calling android. PushbackInputStream. A PushbackInputStream adds functionality to another input stream, namely the ability to "push back" or "unread" bytes, by storing pushed-back bytes in an internal buffer.


1 Answers

while (available() == 0) {
    delay(10)
}

Here you hope you've achieved non-blocking IO using the InputStream. You imagine that the data would be somehow "trickling in" on its own and you could just wait for it to become available so you can pick it up without blocking in the subsequent read() call.

This behavior isn't universal to any InputStream. In fact, it probably only works with a SocketInputStream and there it also has issues: when the remote end has closed the connection, it will keep returning 0 until you make another read call to observe that the socket is closed.

In other implementations of InputStream, available() will always return 0 unless the stream is buffered, in which case it will simply tell you how much is left in the buffer. When the buffer is empty, the input stream implementation won't try to fetch any more data from the underlying resource until you call read().

Therefore I'd suggest at least narrowing down the receiver of your function to SocketInputStream, but for full correctness you should use NIO code instead.

Finally, if you find that for your specific use case the available() loop does work as expected and read() never blocks, then you should drop withContext(IO) because it is implies two costly context switches (to a background thread and back) and its purpose is only to run blocking code off the GUI thread.

like image 136
Marko Topolnik Avatar answered Dec 01 '22 01:12

Marko Topolnik