Search code examples
linuxrustfilesystemsapple-m1

File metadata modified timestamp returning different values in consecutive calls


I have an library I'm implementing that writes some data to a file and records the timestamp. It then checks the timestamp of the written file to see if it has been updated externally. But one of the test is failing^1 occasionally because the fs::metadata(&self.path)?.modified()?; returns different values in consecutive calls despite no change to the file. I'm noticing this behaviour on Mac M1 and Ubuntu on an AMD laptop. Could it be specific to an ARM architecture thing?

The pseudo logic for the test is something like this

let file = create_file
let writer = BufWriter(file)
writer.write_all(b"This test is killing me");
let latest_update = fs::metadata(file_path).modified;
self.modified = latest_update
let check_update = fs::metadata(file_path).modified;

assert_eq!(check_update, latest_update); // fails

The exact failing test is shared in the below link. You might have to run it multiple times to get it to fail.


Solution

  • This can happen if the BufWriter is dropped between the first and second checks.

    This is because BufWriter keeps a buffer of data that may not be written yet, with the intention of delaying until it fills up its buffer. If that is the case when it is dropped, it must issue one last write to the file to finish writing what it was given. Thus the first will see the metadata that is reflective of how it was before the final write and the second will see the consequence of it.

    To avoid this problem, you can .flush() the writer before retrieving the first metadata.

    let file = std::fs::File::create(file_path).unwrap();
    let mut writer = BufWriter::new(file);
    
    writer.write_all(b"This test is killing me").unwrap();
    writer.flush().unwrap(); // this fixes it
    let latest_update = std::fs::metadata(file_path).unwrap().modified().unwrap();
    
    drop(writer); // this is the original problem
    
    let check_update = std::fs::metadata(file_path).unwrap().modified().unwrap();
    
    assert_eq!(check_update, latest_update);