I would like to ask for comments and suggestions on my solution to the following problem. I maintain a frontend to gnuplot, which implements the following plotting process:
- Run gnuplot and connect to it via stdin, stdout, and stderr
- Send plotting commands to gnuplot by writing to its stdin (gnuplot may take an arbitrary amount of time to run these commands)
- Ask gnuplot to print sigil strings to its stdout and stderr
- Read gnuplot’s stdout and stderr
- Go to step 2.
Julia’s documentation and suggested solutions to inter-process communications focus on a scenario where a process starts, returns something, and finishes, which is different than the case described above.
Essentially, the problem I need to solve is to read()
the data available in gnuplot’s stdout and stderr while the pipes are still open and gnuplot is still running. Until now, I’ve been relying on readavailable()
, but that approach is no longer recommended. The difficulties I’m facing are the following:
-
read(p::Pipe)
blocks until the pipe is closed. -
read(p, nb, all=false)
is not supported whenp
is aPipe
- I could
readuntil
the sigil string is read, butreaduntil(p::Pipe, s)
is likewise not supported.
I have come up with a solution based on bytesavailable()
, included below. I have one function to start gnuplot, another to end it, and a main function communicate()
to perform steps 2–4 above. This code is not super polished yet, but it works in my experiments so far. I’d like to ask for comments and suggestions. Thanks in advance!
"Step 1: Start gnuplot process."
function startgp()
inp = Pipe()
out = Pipe()
err = Pipe()
process = run(pipeline(`gnuplot`, stdin=inp, stdout=out, stderr=err), wait=false)
close(out.in)
close(err.in)
# Communication fails without these
Base.start_reading(out.out)
Base.start_reading(err.out)
return (process, inp, out, err)
end
"End gnuplot process `p`"
function endgp(p)
write(p[2], "exit gnuplot\n")
close(p[2])
wait(p[1])
return p[1].exitcode
end
"Send `input` to process `p` and return its response."
function communicate(p, input)
inp = p[2]
out = p[3]
err = p[4]
gpout = @async begin
while true
b = bytesavailable(out)
if b == 0
sleep(0.01)
continue
else
return String(read(out, b))
end
end
end
gperr = @async begin
while true
b = bytesavailable(err)
if b == 0
sleep(0.01)
continue
else
return String(read(err, b))
end
end
end
# Step 2: send user input to gnuplot
write(inp, input)
# Step 3: ask gnuplot to return sigils when it is done
write(inp, """\nset print '-'
print 'Done'
set print
print 'Done'\n""")
# Step 4: read gnuplot's stdout and stderr
return ( fetch(gpout), fetch(gperr) )
end