Pass a richer `State` pointer to fsevents trampoline

Antonio Scandurra created

This will be useful to re-instate a new stream when dropping events.

Change summary

crates/fsevent/src/fsevent.rs | 59 +++++++++++++++++++++++-------------
1 file changed, 37 insertions(+), 22 deletions(-)

Detailed changes

crates/fsevent/src/fsevent.rs 🔗

@@ -22,11 +22,24 @@ pub struct Event {
 
 pub struct EventStream {
     stream: fs::FSEventStreamRef,
-    state: Arc<Mutex<Lifecycle>>,
-    callback: Box<Option<RunCallback>>,
+    lifecycle: Arc<Mutex<Lifecycle>>,
+    state: Box<State>,
 }
 
-type RunCallback = Box<dyn FnMut(Vec<Event>) -> bool>;
+struct State {
+    latency: Duration,
+    paths: cf::CFMutableArrayRef,
+    callback: Option<Box<dyn FnMut(Vec<Event>) -> bool>>,
+    last_valid_event_id: Option<fs::FSEventStreamEventId>,
+}
+
+impl Drop for State {
+    fn drop(&mut self) {
+        unsafe {
+            cf::CFRelease(self.paths);
+        }
+    }
+}
 
 enum Lifecycle {
     New,
@@ -42,15 +55,6 @@ unsafe impl Send for Lifecycle {}
 impl EventStream {
     pub fn new(paths: &[&Path], latency: Duration) -> (Self, Handle) {
         unsafe {
-            let callback = Box::new(None);
-            let stream_context = fs::FSEventStreamContext {
-                version: 0,
-                info: callback.as_ref() as *const _ as *mut c_void,
-                retain: None,
-                release: None,
-                copy_description: None,
-            };
-
             let cf_paths =
                 cf::CFArrayCreateMutable(cf::kCFAllocatorDefault, 0, &cf::kCFTypeArrayCallBacks);
             assert!(!cf_paths.is_null());
@@ -69,6 +73,19 @@ impl EventStream {
                 cf::CFRelease(cf_url);
             }
 
+            let state = Box::new(State {
+                latency,
+                paths: cf_paths,
+                callback: None,
+                last_valid_event_id: None,
+            });
+            let stream_context = fs::FSEventStreamContext {
+                version: 0,
+                info: state.as_ref() as *const _ as *mut c_void,
+                retain: None,
+                release: None,
+                copy_description: None,
+            };
             let stream = fs::FSEventStreamCreate(
                 cf::kCFAllocatorDefault,
                 Self::trampoline,
@@ -80,17 +97,15 @@ impl EventStream {
                     | fs::kFSEventStreamCreateFlagNoDefer
                     | fs::kFSEventStreamCreateFlagWatchRoot,
             );
-            cf::CFRelease(cf_paths);
-
-            let state = Arc::new(Mutex::new(Lifecycle::New));
 
+            let lifecycle = Arc::new(Mutex::new(Lifecycle::New));
             (
                 EventStream {
                     stream,
-                    state: state.clone(),
-                    callback,
+                    lifecycle: lifecycle.clone(),
+                    state,
                 },
-                Handle(state),
+                Handle(lifecycle),
             )
         }
     }
@@ -99,11 +114,11 @@ impl EventStream {
     where
         F: FnMut(Vec<Event>) -> bool + 'static,
     {
-        *self.callback = Some(Box::new(f));
+        self.state.callback = Some(Box::new(f));
         unsafe {
             let run_loop = cf::CFRunLoopGetCurrent();
             {
-                let mut state = self.state.lock();
+                let mut state = self.lifecycle.lock();
                 match *state {
                     Lifecycle::New => *state = Lifecycle::Running(run_loop),
                     Lifecycle::Running(_) => unreachable!(),
@@ -129,8 +144,8 @@ impl EventStream {
             let event_paths = event_paths as *const *const ::std::os::raw::c_char;
             let e_ptr = event_flags as *mut u32;
             let i_ptr = event_ids as *mut u64;
-            let callback_ptr = (info as *mut Option<RunCallback>).as_mut().unwrap();
-            let callback = if let Some(callback) = callback_ptr.as_mut() {
+            let state = (info as *mut State).as_mut().unwrap();
+            let callback = if let Some(callback) = state.callback.as_mut() {
                 callback
             } else {
                 return;