In the below code I'm copying a file(worker thread) and want to print the progress of the file copy operation in main thread. I would like to know, if it's possible to get the file/seek position of the reader file opened in worker thread asynchronously, so that progress info can be notified to main thread. This is what I've written so far
use std::io::{Read, Write};
use std::fs;
use std::sync::{Arc, Condvar, Mutex};
fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
let mut buf = vec![0; 1024];
let mut bytes_read = buf.capacity();
while bytes_read > 0 {
bytes_read = reader.read(&mut buf).expect("failed to read from the file");
if bytes_read > 0 {
writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
}
}
writer.flush().expect("failed to flush the writer");
}
fn foo(s: Arc<(Condvar, Mutex<i32>)>) {
let &(ref cv, ref m) = &*s;
let src_file = fs::File::open("~/a.txt").expect("unable to open the file");
let dst_file = fs::File::create("~/b.txt").expect("unable to create the file");
write_to_file(src_file, dst_file);
let mut progress = 0;
// get the progress of above file operation using file/seek position of src file
// if possible using async and store it in above progress
// I cannot change the syntax of write_to_file
{
let mut mg = m.lock().unwrap();
*mg = progress;
}
cv.notify_one();
}
fn main() {
let s = Arc::new((Condvar::new(), Mutex::new(0)));
let sa = Arc::clone(&s);
let jh = std::thread::spawn(move || {
foo(sa);
});
let mut prev_progress = 0;
let &(ref cv, ref m) = &*s;
loop {
let mut mg = m.lock().unwrap();
while *mg == prev_progress {
mg = cv.wait(mg).unwrap();
}
prev_progress = *mg;
println!("{}", *mg);
if *mg == 100 {
break;
}
}
jh.join();
}
Since &File
implements both Read
and Seek
you can simply pass references instead of the values themselves. That gives your foo
function access the &File
and it's Seek
implementation after you pass one copy to write_to_file
:
fn foo(s: Arc<(Condvar, Mutex<u64>)>) {
let &(ref cv, ref m) = &*s;
let mut src_file = &fs::File::open("src/main.rs").expect("unable to open the file");
let dst_file = &fs::File::create("b.txt").expect("unable to create the file");
std::thread::scope(|s| {
let w = s.spawn(move || {
write_to_file(src_file, dst_file);
});
while !w.is_finished() {
std::thread::sleep_ms(1);
let progress = src_file.stream_position().unwrap();
let total = src_file.metadata().unwrap().len();
{
let mut mg = m.lock().unwrap();
*mg = 100 * progress / total;
}
cv.notify_one();
}
*m.lock().unwrap() = 100;
});
}
If you can't easily pass around shared references to the File
, for example because you want to open the File
in a function, you can also use Arc<File>
and clone that instead since it also implements Read
in this way you can share the ownership and avoid having to use a non-static reference.
Since the asynchronous versions of File
do not implement AsyncRead
/AsyncSeek
for references you can't use them in a similar manner.