Consistency/Concurrency problems when using linux inoitfy

74 views Asked by At

I am working a project, which is to build a simple file synchronizer, using Rust language.

To monitor the changes in local files, I should use the linux utility inotify. However, when a new creation (file or directory) is detected, I should add new watches to the inotify instance, using the syscall inotify_add_watch.

Handling this using Rust seems simple, I can have an inotify instance and can add watches using inotify.watches().add(), read the events using inotify.read_events_blocking() functions.

However, I may want to wait for events while adding new watches into the inotify instance, and the adding operations may be handled on multi-threads (which is actually for the newly synchronized files to add watches for them. So I need the waiting for local changes at the same time).

I wonder, would there be conflicts in these scenarios :

  • One thread is reading block the inotify instace, while another thread is adding a new watch.
  • Multiple threads are trying to add different files' wathces at the same time.

By checking the linux man page, I got that all these operations are actually handled by the inotify instance's file descriptor, which means, all the operations above are using the same file descriptor. But I didn't find the man page saying there would be inconsistency problems.

And I also write a simple experiments to check the first senario

fn main() {
    let mut inotify = Inotify::init().unwrap();
    std::thread::spawn(move || {
        let mut buffer = [0; 1024];
        loop {
            let events = inotify.read_events_blocking(buffer.as_mut()).unwrap();
            for event in events {
                println!("{:?}", event);
            }
        }
    });
    loop {
        // input a file path
        let mut file_path = String::new();
        std::io::stdin().read_line(&mut file_path).unwrap();
        let file_path = file_path.trim();
        inotify.watches().add(file_path, *WATCH_EVENTS).unwrap();
        println!("watching {}", file_path);
    }
}

It works well, but I am still not sure about that.

If all the above oprations are atomic, here comes a new problem

Read events requires mutable reference to inotify instance

    pub fn read_events_blocking<'a>(&mut self, buffer: &'a mut [u8])

But adding a new watch should use the Watch interface, which is actually a private struct (I cannot have the interface in another thread)

    pub fn watches(&self) -> Watches {
        Watches::new(self.fd.clone())
    }

===============================updates==============================

At the version 0.10.0, the interface Watches to add or remove watches is private, but I just found the at the latest version 0.10.2, they made it public !

So I guess it totally safe to use that.

0

There are 0 answers