From 4f410f32feba97584c40b77e4ff7ee150e003b13 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 7 Jun 2021 18:21:25 -0700 Subject: [PATCH 1/3] Make EventFn take FnMut This changes EventFn to work with FnMut, rather than Fn. The advantage of this is that this allows callers to use state in the EventFn, rather than having to use interior mutability. This is helpful particularly for getting notify to work with async rust, since `futures::channel::mpsc`'s methods all take `&mut self`. The downside of this change is that it limits the ability of notify to share `EventFn` across multiple threads, but all the backends either guarantee only a single background thread, or they already use an `Arc>` to protect `EventFn`. --- src/fsevent.rs | 2 +- src/inotify.rs | 8 ++++---- src/lib.rs | 4 ++-- src/poll.rs | 4 ++-- src/windows.rs | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/fsevent.rs b/src/fsevent.rs index 9018c8c1..5174c447 100644 --- a/src/fsevent.rs +++ b/src/fsevent.rs @@ -534,7 +534,7 @@ unsafe fn callback_impl( for ev in translate_flags(flag, true).into_iter() { // TODO: precise let ev = ev.add_path(path.clone()); - let event_fn = event_fn.lock().expect("lock not to be poisoned"); + let mut event_fn = event_fn.lock().expect("lock not to be poisoned"); (event_fn)(Ok(ev)); } } diff --git a/src/inotify.rs b/src/inotify.rs index 675a7284..4829fc00 100644 --- a/src/inotify.rs +++ b/src/inotify.rs @@ -56,7 +56,7 @@ enum EventLoopMsg { } #[inline] -fn send_pending_rename_event(rename_event: &mut Option, event_fn: &dyn EventFn) { +fn send_pending_rename_event(rename_event: &mut Option, event_fn: &mut dyn EventFn) { if let Some(e) = rename_event.take() { event_fn(Ok(e)); } @@ -188,7 +188,7 @@ impl EventLoop { let current_cookie = self.rename_event.as_ref().and_then(|e| e.tracker()); // send pending rename event only if the rename event for which the timer has been created hasn't been handled already; otherwise ignore this timeout if current_cookie == Some(cookie) { - send_pending_rename_event(&mut self.rename_event, &*self.event_fn); + send_pending_rename_event(&mut self.rename_event, &mut *self.event_fn); } } EventLoopMsg::Configure(config, tx) => { @@ -229,7 +229,7 @@ impl EventLoop { }; if event.mask.contains(EventMask::MOVED_FROM) { - send_pending_rename_event(&mut self.rename_event, &*self.event_fn); + send_pending_rename_event(&mut self.rename_event, &mut *self.event_fn); remove_watch_by_event(&path, &self.watches, &mut remove_watches); self.rename_event = Some( Event::new(EventKind::Modify(ModifyKind::Name( @@ -384,7 +384,7 @@ impl EventLoop { if !evs.is_empty() { send_pending_rename_event( &mut self.rename_event, - &*self.event_fn, + &mut *self.event_fn, ); } diff --git a/src/lib.rs b/src/lib.rs index b379b7b0..41ca40a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,9 +127,9 @@ mod config; mod error; /// The set of requirements for watcher event handling functions. -pub trait EventFn: 'static + Fn(Result) + Send {} +pub trait EventFn: 'static + FnMut(Result) + Send {} -impl EventFn for F where F: 'static + Fn(Result) + Send {} +impl EventFn for F where F: 'static + FnMut(Result) + Send {} /// Type that can deliver file activity notifications /// diff --git a/src/poll.rs b/src/poll.rs index 42f847b2..750edb77 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -36,8 +36,8 @@ pub struct PollWatcher { } fn emit_event(event_fn: &Mutex, res: Result) { - if let Ok(guard) = event_fn.lock() { - let f: &dyn EventFn = &*guard; + if let Ok(mut guard) = event_fn.lock() { + let f: &mut dyn EventFn = &mut *guard; f(res); } } diff --git a/src/windows.rs b/src/windows.rs index 620b621e..ffe77045 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -346,8 +346,8 @@ unsafe extern "system" fn handle_event( let newe = Event::new(EventKind::Any).add_path(path); fn emit_event(event_fn: &Mutex, res: Result) { - if let Ok(guard) = event_fn.lock() { - let f: &dyn EventFn = &*guard; + if let Ok(mut guard) = event_fn.lock() { + let f: &mut dyn EventFn = &mut *guard; f(res); } } From dca87cb78ecac2507956e9daca8a832949a177d7 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 7 Jun 2021 21:07:32 -0700 Subject: [PATCH 2/3] Add async example This adds a new example on how to lift events from the synchronous code into async rust. --- Cargo.toml | 1 + examples/async_monitor.rs | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 examples/async_monitor.rs diff --git a/Cargo.toml b/Cargo.toml index 67b0fd86..7c50a18f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ fsevent-sys = "4" winapi = { version = "0.3.8", features = ["fileapi", "handleapi", "ioapiset", "minwinbase", "synchapi", "winbase", "winnt"] } [dev-dependencies] +futures = "0.3" serde_json = "1.0.39" tempfile = "3.2.0" diff --git a/examples/async_monitor.rs b/examples/async_monitor.rs new file mode 100644 index 00000000..5dccb68a --- /dev/null +++ b/examples/async_monitor.rs @@ -0,0 +1,47 @@ +use notify::{RecommendedWatcher, RecursiveMode, Event, Watcher}; +use std::path::Path; +use futures::{SinkExt, StreamExt, channel::mpsc::{channel, Receiver}}; + +fn async_watcher() -> notify::Result<(RecommendedWatcher, Receiver>)> { + let (mut tx, rx) = channel(1); + + // Automatically select the best implementation for your platform. + // You can also access each implementation directly e.g. INotifyWatcher. + let watcher = Watcher::new_immediate(move |res| { + futures::executor::block_on(async { + tx.send(res).await.unwrap(); + }) + })?; + + Ok((watcher, rx)) +} + +async fn async_watch>(path: P) -> notify::Result<()> { + let (mut watcher, mut rx) = async_watcher()?; + + // Add a path to be watched. All files and directories at that path and + // below will be monitored for changes. + watcher.watch(path, RecursiveMode::Recursive)?; + + while let Some(res) = rx.next().await { + match res { + Ok(event) => println!("changed: {:?}", event), + Err(e) => println!("watch error: {:?}", e), + } + } + + Ok(()) +} + +fn main() { + let path = std::env::args() + .nth(1) + .expect("Argument 1 needs to be a path"); + println!("watching {}", path); + + futures::executor::block_on(async { + if let Err(e) = async_watch(path).await { + println!("error: {:?}", e) + } + }); +} From 9d282fb140d77e9e91a2feb14608b0f574714a4e Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 7 Jun 2021 21:17:19 -0700 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ec7fee1..f303456d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ ## unreleased +- CHANGE: Change EventFn to take FnMut [#333] + +[#333]: https://github.com/notify-rs/notify/pull/333 + ## 5.0.0-pre.10 (2021-06-04) - FIX: Make StreamContextInfo `Send` to fix soundness issue [#325]