mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 12:02:07 +03:00
Restart FSEventStream at the last seen event when "dropped" is reported
This commit is contained in:
parent
f3bc4feaf0
commit
663173d2f5
@ -8,7 +8,7 @@ use std::{
|
|||||||
ffi::{c_void, CStr, OsStr},
|
ffi::{c_void, CStr, OsStr},
|
||||||
os::unix::ffi::OsStrExt,
|
os::unix::ffi::OsStrExt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
slice,
|
ptr, slice,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@ -21,7 +21,6 @@ pub struct Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventStream {
|
pub struct EventStream {
|
||||||
stream: fs::FSEventStreamRef,
|
|
||||||
lifecycle: Arc<Mutex<Lifecycle>>,
|
lifecycle: Arc<Mutex<Lifecycle>>,
|
||||||
state: Box<State>,
|
state: Box<State>,
|
||||||
}
|
}
|
||||||
@ -31,6 +30,7 @@ struct State {
|
|||||||
paths: cf::CFMutableArrayRef,
|
paths: cf::CFMutableArrayRef,
|
||||||
callback: Option<Box<dyn FnMut(Vec<Event>) -> bool>>,
|
callback: Option<Box<dyn FnMut(Vec<Event>) -> bool>>,
|
||||||
last_valid_event_id: Option<fs::FSEventStreamEventId>,
|
last_valid_event_id: Option<fs::FSEventStreamEventId>,
|
||||||
|
stream: fs::FSEventStreamRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for State {
|
impl Drop for State {
|
||||||
@ -73,11 +73,12 @@ impl EventStream {
|
|||||||
cf::CFRelease(cf_url);
|
cf::CFRelease(cf_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = Box::new(State {
|
let mut state = Box::new(State {
|
||||||
latency,
|
latency,
|
||||||
paths: cf_paths,
|
paths: cf_paths,
|
||||||
callback: None,
|
callback: None,
|
||||||
last_valid_event_id: None,
|
last_valid_event_id: None,
|
||||||
|
stream: ptr::null_mut(),
|
||||||
});
|
});
|
||||||
let stream_context = fs::FSEventStreamContext {
|
let stream_context = fs::FSEventStreamContext {
|
||||||
version: 0,
|
version: 0,
|
||||||
@ -97,11 +98,11 @@ impl EventStream {
|
|||||||
| fs::kFSEventStreamCreateFlagNoDefer
|
| fs::kFSEventStreamCreateFlagNoDefer
|
||||||
| fs::kFSEventStreamCreateFlagWatchRoot,
|
| fs::kFSEventStreamCreateFlagWatchRoot,
|
||||||
);
|
);
|
||||||
|
state.stream = stream;
|
||||||
|
|
||||||
let lifecycle = Arc::new(Mutex::new(Lifecycle::New));
|
let lifecycle = Arc::new(Mutex::new(Lifecycle::New));
|
||||||
(
|
(
|
||||||
EventStream {
|
EventStream {
|
||||||
stream,
|
|
||||||
lifecycle: lifecycle.clone(),
|
lifecycle: lifecycle.clone(),
|
||||||
state,
|
state,
|
||||||
},
|
},
|
||||||
@ -125,10 +126,16 @@ impl EventStream {
|
|||||||
Lifecycle::Stopped => return,
|
Lifecycle::Stopped => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs::FSEventStreamScheduleWithRunLoop(self.stream, run_loop, cf::kCFRunLoopDefaultMode);
|
fs::FSEventStreamScheduleWithRunLoop(
|
||||||
fs::FSEventStreamStart(self.stream);
|
self.state.stream,
|
||||||
|
run_loop,
|
||||||
|
cf::kCFRunLoopDefaultMode,
|
||||||
|
);
|
||||||
|
fs::FSEventStreamStart(self.state.stream);
|
||||||
cf::CFRunLoopRun();
|
cf::CFRunLoopRun();
|
||||||
fs::FSEventStreamRelease(self.stream);
|
fs::FSEventStreamStop(self.state.stream);
|
||||||
|
fs::FSEventStreamInvalidate(self.state.stream);
|
||||||
|
fs::FSEventStreamRelease(self.state.stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,30 +161,83 @@ impl EventStream {
|
|||||||
let paths = slice::from_raw_parts(event_paths, num);
|
let paths = slice::from_raw_parts(event_paths, num);
|
||||||
let flags = slice::from_raw_parts_mut(e_ptr, num);
|
let flags = slice::from_raw_parts_mut(e_ptr, num);
|
||||||
let ids = slice::from_raw_parts_mut(i_ptr, num);
|
let ids = slice::from_raw_parts_mut(i_ptr, num);
|
||||||
|
let mut stream_restarted = false;
|
||||||
|
|
||||||
let mut events = Vec::with_capacity(num);
|
// Sometimes FSEvents reports a "dropped" event, an indication that either the kernel
|
||||||
for p in 0..num {
|
// or our code couldn't keep up with the sheer volume of file-system events that were
|
||||||
let path_c_str = CStr::from_ptr(paths[p]);
|
// generated. If we observed a valid event before this happens, we'll try to read the
|
||||||
let path = PathBuf::from(OsStr::from_bytes(path_c_str.to_bytes()));
|
// file-system journal by stopping the current stream and creating a new one starting at
|
||||||
if let Some(flag) = StreamFlags::from_bits(flags[p]) {
|
// such event. Otherwise, we'll let invoke the callback with the dropped event, which
|
||||||
if flag.contains(StreamFlags::HISTORY_DONE) {
|
// will likely perform a re-scan of one of the root directories.
|
||||||
events.clear();
|
if flags
|
||||||
} else {
|
.iter()
|
||||||
events.push(Event {
|
.copied()
|
||||||
event_id: ids[p],
|
.filter_map(StreamFlags::from_bits)
|
||||||
flags: flag,
|
.any(|flags| {
|
||||||
path,
|
flags.contains(StreamFlags::USER_DROPPED)
|
||||||
});
|
|| flags.contains(StreamFlags::KERNEL_DROPPED)
|
||||||
}
|
})
|
||||||
} else {
|
{
|
||||||
debug_assert!(false, "unknown flag set for fs event: {}", flags[p]);
|
if let Some(last_valid_event_id) = state.last_valid_event_id.take() {
|
||||||
|
fs::FSEventStreamStop(state.stream);
|
||||||
|
fs::FSEventStreamInvalidate(state.stream);
|
||||||
|
fs::FSEventStreamRelease(state.stream);
|
||||||
|
|
||||||
|
let stream_context = fs::FSEventStreamContext {
|
||||||
|
version: 0,
|
||||||
|
info,
|
||||||
|
retain: None,
|
||||||
|
release: None,
|
||||||
|
copy_description: None,
|
||||||
|
};
|
||||||
|
let stream = fs::FSEventStreamCreate(
|
||||||
|
cf::kCFAllocatorDefault,
|
||||||
|
Self::trampoline,
|
||||||
|
&stream_context,
|
||||||
|
state.paths,
|
||||||
|
last_valid_event_id,
|
||||||
|
state.latency.as_secs_f64(),
|
||||||
|
fs::kFSEventStreamCreateFlagFileEvents
|
||||||
|
| fs::kFSEventStreamCreateFlagNoDefer
|
||||||
|
| fs::kFSEventStreamCreateFlagWatchRoot,
|
||||||
|
);
|
||||||
|
|
||||||
|
state.stream = stream;
|
||||||
|
fs::FSEventStreamScheduleWithRunLoop(
|
||||||
|
state.stream,
|
||||||
|
cf::CFRunLoopGetCurrent(),
|
||||||
|
cf::kCFRunLoopDefaultMode,
|
||||||
|
);
|
||||||
|
fs::FSEventStreamStart(state.stream);
|
||||||
|
stream_restarted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !events.is_empty() {
|
if !stream_restarted {
|
||||||
if !callback(events) {
|
let mut events = Vec::with_capacity(num);
|
||||||
fs::FSEventStreamStop(stream_ref);
|
for p in 0..num {
|
||||||
cf::CFRunLoopStop(cf::CFRunLoopGetCurrent());
|
if let Some(flag) = StreamFlags::from_bits(flags[p]) {
|
||||||
|
if !flag.contains(StreamFlags::HISTORY_DONE) {
|
||||||
|
let path_c_str = CStr::from_ptr(paths[p]);
|
||||||
|
let path = PathBuf::from(OsStr::from_bytes(path_c_str.to_bytes()));
|
||||||
|
let event = Event {
|
||||||
|
event_id: ids[p],
|
||||||
|
flags: flag,
|
||||||
|
path,
|
||||||
|
};
|
||||||
|
state.last_valid_event_id = Some(event.event_id);
|
||||||
|
events.push(event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug_assert!(false, "unknown flag set for fs event: {}", flags[p]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !events.is_empty() {
|
||||||
|
if !callback(events) {
|
||||||
|
fs::FSEventStreamStop(stream_ref);
|
||||||
|
cf::CFRunLoopStop(cf::CFRunLoopGetCurrent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user