Hi everyone, I hope you are all doing well. I am f...
# coroutines
d
Hi everyone, I hope you are all doing well. I am facing an issue with
runInterruptible
I am using it to make a blocking IO call cancellable Here is the code
Copy code
suspend fun foo () {
withContext(ioDispatcher){
    runInterruptible{
        print("reading")
        inputStream.read(buffer)
        print("finish reading")
    }
}
}

val job = scope.launch{foo()}
delay(200)
job.cancelAndJoin()
What happens is that as output I get reading finish reading Shouldn't it be just reading Is this the expected behaviour?
j
As far as I know,
InputStream
reads are not interruptible, so you can't use
runInterruptible
like you're trying to do.
Even if it were, the code you show here could still behave as you show if the input stream contains data and the buffer is small enough to read the data in less than 200ms
d
is there a way to make
InputStream.read()
function interruptible so that when it is cancelled it stops the reading
Also, I added delay for clarification, we can assume that
read(buffer)
operation takes longer than 200ms
e
afaik you need to use NIO for interruptible file IO
r
It's impossible to make the Stream API interruptible in a way that will work as you expect with this code because you'd need to throw InterruptedException, which is a checked exception in Java and not declared in the interface
Interestingly some old JVM implementations tried to handle interruption by returning end of stream immediately but this caused more problems than it solved so it's now not done by any standard implementation
d
Even using
bufferedReader
and
bufferedWriter
from
nio
caused the same issue.
but like, what to use runInterruptible for, I thought this was the ideal scenario 😂
e
you need to use channels I believe
d
okay let me try that also
e
but also, on Linux, file IO isn't really interruptible anyway
d
@ephemient, thank you, channels work much faster, and it is much smoother overall
runInterruptible
doesn't make any difference, it works the same with and without it so I decided to just remove it and put
yield()
between read and write operations It's just that
runInterruptible
doesn't work in this case. The only thing I can now assume is that what @Robert Williams said was right, the only way to interrupt it is to throw
InterruptedException
and since runInterruptible will throw (I assume)
CancellationException
it doesn't work
j
> but like, what to use runInterruptible for, I thought this was the ideal scenario
runInterruptible
is useful for anything that actually supports interruption (e.g.
Thread.sleep()
, or
BlockingQueue.take()
). If your current coroutine is cancelled, the cancellation will be turned into an interruption of the thread for the code inside the
runInterruptible
lambda. But if the code inside the lambda doesn't react to interruptions, it's pointless. It is just as pointless as cancelling a coroutine that's running non-cooperative (non-cancellable) code, like blocking code.
d
Yes yes, got it, thank you @Joffrey
j
And indeed last time I bridged
ProcessBuilder
with coroutines, I initially added some
yield()
between the standard stream reads. I then changed this to
coroutineContext.ensureActive()
to avoid unnecessary suspensions: https://github.com/JetBrains/amper/blob/0.2/sources/cli/src/org/jetbrains/amper/processes/Processes.kt#L82
d
Oh yes, I see. Makes sense in almost all cases, but for me it's enough to keep the connection alive 😄 so I will keep
yield()
since I don't really care about data transfered through these streams
j
Not sure I understand your point 🤔
d
I need
inputStream
alive as much as possible so it's okay for
yield
to make it slower sometimes :D
j
But
yield
will also throw when cancelled
d
yes, that's desired behaviour. If it doesn't throw it will possibly give resources to another coroutine making the
inputStream
still open :D
There is no need to occupy the whole thread all the time for me
Copy code
while(!EOF){
    read()
    yield()
     write
}
will not block the thread, while
Copy code
while(!EOF){
    read()
    ensureActive()
     write
}
will For me it's important to have the input stream connection open, not to read/write from it 😄
j
I see, makes sense
👌 1