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