Yeah, unsafe_write
is probably too unsafe, especially while I don’t understand every detail of asynchronous I/O. Also copyuntil
doesn’t seem to work with Pipe
/PipeEndpoint
objects.
It would be nice if there was an atomic
bytes_read = read!(pip, buffer, bufsize)
, but I don’t think there is.
Actually, looking at the internals, with @edit read(pipe, bufsize)
, I think there is, and it’s readbytes!
.
So this is my latest iteration, which seems to work and shouldn’t allocate anything inside the loop:
bufsize = 128
buffer = Vector{UInt8}(undef, bufsize)
buffer_redirect_task = @async begin
while true
nbytes = readbytes!(pipe, buffer, bufsize)
data = view(buffer, 1:nbytes)
write(output, data)
write(default_stdout, data)
isopen(pipe) || break
end
end
Now, one more question about the exit condition from the loop (since I’m still pretty unsure about some of the details of asynchronous I/O): Is there any possibility that I might be missing data because the main task puts data into the pipe between the time I call readbytes!(pipe, buffer, bufsize)
and the time I call isopen(pipe)
? What about if I move the isopen
check to the beginning of the loop (while isopen(pipe)
)?
The other possibility I was considering was (nbytes == 0) && break
, which also seems to work in my tests. But again: is there any possibility that the main task doesn’t write data to the pipe fast enough, so that there (temporarily) might not be anything for readbytes!
to read, making me believe erroneously that the main task is done? Update: Let’s split this off to Race condition reading from Pipe. The MWE there demonstrated that (nbytes == 0)
does not work in general.
I saw iolock_begin()
and iolock_end()
in the internals of Pipe
. Is that something I might need here to avoid race conditions?