Open jonasbb opened 5 years ago
As a workaround, as of version 0.0.27 you can do fg.set_post_commands("unset output").show()
and that should finish writing the file.
Thanks for the feedback. I will try this with the new version.
@SiegeLord Unfortunately, specifying a post command still leaves the race condition.
This is the exact code I used. Make sure to remove the /tmp/plot.png
file before executing it, in case it still exists from a previous run.
use gnuplot::Figure;
use std::{fs, path::Path, thread};
fn main() {
let fpath = Path::new("/tmp/plot.png");
let mut fg = Figure::new();
fg.axes2d().boxes(0..5, 0..5, &[]);
fg.set_terminal("pngcairo", &*fpath.to_string_lossy());
fg.set_post_commands("unset output").show();
// Now read the file again
// thread::sleep_ms(1000); // <== If this line is commented out, the next unwrap fails. Otherwise it works.
let res = fs::read(fpath).unwrap();
println!("{:?}", res);
}
The problem is that in the code which sends the print commands to gnuplot, there is no way to wait for gnuplot to finish writing the file.
This code writes the commands to stdin of gnuplot. This might be buffered. The code continues as soon as writing to stdin succeeded. Now, it will take some time for gnuplot to process the commands and write the final output file. During the time gnuplot draws, the Rust code continues and tries to read the file, which was not yet written. The gnuplot
field is private to the Figure
instance, so there is no way for outside code to block until gnuplot finished writing the file. This leaves the unavoidable race condition for any code using the show
function
My workaround is this:
// Start gnuplot process
let mut child = Command::new("gnuplot-nox")
.stdin(Stdio::piped())
.spawn()
.unwrap();
fg.echo(
child
.stdin
.as_mut()
.expect("Stdin exists, because we configured it"),
);
child.wait().unwrap(); // <== this line waits untils gnuplot has finished executing. At that point the output file must exist
I spawn a new gnuplot process for each image I want to draw, without the --persist
flag. This way I can wait on the gnuplot process to finish before executing more Rust code. After the gnuplot process finished, the image file should have been written.
Ok, I see. What did is that as of version 0.0.29 Figure
now has a close
method which closes the gnuplot
process, hopefully accomplishing the same thing as your code. Here's an excerpt from the test I added:
use std::fs;
use tempfile::TempDir;
let tmp_path = TempDir::new().unwrap().into_path();
let file_path = tmp_path.join("plot.png");
let mut fg = Figure::new();
fg.axes2d().boxes(0..5, 0..5, &[]);
fg.set_terminal("pngcairo", &*file_path.to_string_lossy());
fg.show().close();
fs::read(file_path).unwrap();
fs::remove_dir_all(&tmp_path);
I.e. add a close
call after show
, no more set_post_commands
necessary.
I still think there's some way to get it working without shutting down the entire process, but that'll have to wait for the future.
I want to create a plot using gnuplot and directly afterwards read the file and process it further. Something like:
However, reading the file fails with "No such file or directory". Putting a sleep before the read "solves" the problem. As such, the problem seems to be, that
show()
returns, before the file is actually shown/created.Is there a way to wait for the gnuplot process to finish creating the output file, before
show()
returns? The only thing I can imagine right now, is spawning a new gnuplot process for each image and using the termination of the process as a signal that the output file was created.