Fully halt background scanner threads when dropping Worktree

* Rework fsevent API to expose a handle for halting the event stream

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld
2021-04-20 15:55:29 -07:00
co-authored by Nathan Sobo
parent 9fd8acdce7
commit b20f5e9139
6 changed files with 158 additions and 68 deletions
+46 -40
View File
@@ -37,8 +37,9 @@ enum ScanState {
pub struct Worktree {
snapshot: Snapshot,
scanner: Arc<BackgroundScanner>,
background_snapshot: Arc<Mutex<Snapshot>>,
scan_state: (watch::Sender<ScanState>, watch::Receiver<ScanState>),
_event_stream_handle: fsevent::Handle,
poll_scheduled: bool,
}
@@ -50,25 +51,33 @@ pub struct FileHandle {
impl Worktree {
pub fn new(path: impl Into<Arc<Path>>, ctx: &mut ModelContext<Self>) -> Self {
let scan_state = smol::channel::unbounded();
let (scan_state_tx, scan_state_rx) = smol::channel::unbounded();
let id = ctx.model_id();
let snapshot = Snapshot {
id: ctx.model_id(),
id,
path: path.into(),
root_inode: None,
entries: Default::default(),
};
let scanner = Arc::new(BackgroundScanner::new(snapshot.clone(), scan_state.0));
let (event_stream, event_stream_handle) =
fsevent::EventStream::new(&[snapshot.path.as_ref()], Duration::from_millis(100));
let background_snapshot = Arc::new(Mutex::new(snapshot.clone()));
let tree = Self {
snapshot,
scanner,
background_snapshot: background_snapshot.clone(),
scan_state: watch::channel_with(ScanState::Scanning),
_event_stream_handle: event_stream_handle,
poll_scheduled: false,
};
let scanner = tree.scanner.clone();
std::thread::spawn(move || scanner.run());
std::thread::spawn(move || {
let scanner = BackgroundScanner::new(background_snapshot, scan_state_tx, id);
scanner.run(event_stream)
});
ctx.spawn_stream(scan_state.1, Self::observe_scan_state, |_, _| {})
ctx.spawn_stream(scan_state_rx, Self::observe_scan_state, |_, _| {})
.detach();
tree
@@ -90,7 +99,7 @@ impl Worktree {
}
fn poll_entries(&mut self, ctx: &mut ModelContext<Self>) {
self.snapshot = self.scanner.snapshot();
self.snapshot = self.background_snapshot.lock().clone();
ctx.notify();
if self.is_scanning() && !self.poll_scheduled {
@@ -490,17 +499,17 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for FileCount {
}
struct BackgroundScanner {
snapshot: Mutex<Snapshot>,
snapshot: Arc<Mutex<Snapshot>>,
notify: Sender<ScanState>,
thread_pool: scoped_pool::Pool,
}
impl BackgroundScanner {
fn new(snapshot: Snapshot, notify: Sender<ScanState>) -> Self {
fn new(snapshot: Arc<Mutex<Snapshot>>, notify: Sender<ScanState>, worktree_id: usize) -> Self {
Self {
snapshot: Mutex::new(snapshot),
snapshot,
notify,
thread_pool: scoped_pool::Pool::new(16, "background-scanner"),
thread_pool: scoped_pool::Pool::new(16, format!("worktree-{}-scanner", worktree_id)),
}
}
@@ -512,28 +521,7 @@ impl BackgroundScanner {
self.snapshot.lock().clone()
}
fn run(&self) {
let path = self.snapshot.lock().path.clone();
// Create the event stream before we start scanning to ensure we receive events for changes
// that occur in the middle of the scan.
let event_stream =
fsevent::EventStream::new(&[path.as_ref()], Duration::from_millis(100), |events| {
if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
return false;
}
if !self.process_events(events) {
return false;
}
if smol::block_on(self.notify.send(ScanState::Idle)).is_err() {
return false;
}
true
});
fn run(self, event_stream: fsevent::EventStream) {
if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
return;
}
@@ -548,7 +536,21 @@ impl BackgroundScanner {
return;
}
event_stream.run();
event_stream.run(move |events| {
if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
return false;
}
if !self.process_events(events) {
return false;
}
if smol::block_on(self.notify.send(ScanState::Idle)).is_err() {
return false;
}
true
});
}
fn scan_dirs(&self) -> io::Result<()> {
@@ -945,6 +947,8 @@ mod tests {
);
})
});
eprintln!("HI");
}
#[test]
@@ -1045,13 +1049,14 @@ mod tests {
let (notify_tx, _notify_rx) = smol::channel::unbounded();
let scanner = BackgroundScanner::new(
Snapshot {
Arc::new(Mutex::new(Snapshot {
id: 0,
path: root_dir.path().into(),
root_inode: None,
entries: Default::default(),
},
})),
notify_tx,
0,
);
scanner.scan_dirs().unwrap();
@@ -1073,13 +1078,14 @@ mod tests {
let (notify_tx, _notify_rx) = smol::channel::unbounded();
let new_scanner = BackgroundScanner::new(
Snapshot {
Arc::new(Mutex::new(Snapshot {
id: 0,
path: root_dir.path().into(),
root_inode: None,
entries: Default::default(),
},
})),
notify_tx,
1,
);
new_scanner.scan_dirs().unwrap();
assert_eq!(scanner.snapshot().to_vec(), new_scanner.snapshot().to_vec());