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
inotifyinstace, 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.