Shutdown/cancelation story for main cargo watch thread

This commit is contained in:
Emil Lauridsen 2019-12-27 11:43:05 +01:00
parent a2d10694cc
commit 02ce5bbf6b

View file

@ -32,12 +32,13 @@ pub struct CheckOptions {
/// CheckWatcher wraps the shared state and communication machinery used for /// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing /// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output. /// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)] #[derive(Debug)]
pub struct CheckWatcher { pub struct CheckWatcher {
pub task_recv: Receiver<CheckTask>, pub task_recv: Receiver<CheckTask>,
pub cmd_send: Sender<CheckCommand>, pub cmd_send: Sender<CheckCommand>,
pub shared: Arc<RwLock<CheckWatcherSharedState>>, pub shared: Arc<RwLock<CheckWatcherSharedState>>,
handle: JoinHandle<()>, handle: Option<JoinHandle<()>>,
} }
impl CheckWatcher { impl CheckWatcher {
@ -52,8 +53,7 @@ impl CheckWatcher {
let mut check = CheckWatcherState::new(options, workspace_root, shared_); let mut check = CheckWatcherState::new(options, workspace_root, shared_);
check.run(&task_send, &cmd_recv); check.run(&task_send, &cmd_recv);
}); });
CheckWatcher { task_recv, cmd_send, handle: Some(handle), shared }
CheckWatcher { task_recv, cmd_send, handle, shared }
} }
/// Schedule a re-start of the cargo check worker. /// Schedule a re-start of the cargo check worker.
@ -62,13 +62,21 @@ impl CheckWatcher {
} }
} }
pub struct CheckWatcherState { impl std::ops::Drop for CheckWatcher {
options: CheckOptions, fn drop(&mut self) {
workspace_root: PathBuf, if let Some(handle) = self.handle.take() {
running: bool, // Replace our reciever with dummy one, so we can drop and close the
watcher: WatchThread, // one actually communicating with the thread
last_update_req: Option<Instant>, let recv = std::mem::replace(&mut self.task_recv, crossbeam_channel::never());
shared: Arc<RwLock<CheckWatcherSharedState>>,
// Dropping the original reciever finishes the thread loop
drop(recv);
// Join the thread, it should finish shortly. We don't really care
// whether it panicked, so it is safe to ignore the result
let _ = handle.join();
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -153,6 +161,14 @@ pub enum CheckCommand {
Update, Update,
} }
struct CheckWatcherState {
options: CheckOptions,
workspace_root: PathBuf,
watcher: WatchThread,
last_update_req: Option<Instant>,
shared: Arc<RwLock<CheckWatcherSharedState>>,
}
impl CheckWatcherState { impl CheckWatcherState {
pub fn new( pub fn new(
options: CheckOptions, options: CheckOptions,
@ -163,7 +179,6 @@ impl CheckWatcherState {
CheckWatcherState { CheckWatcherState {
options, options,
workspace_root, workspace_root,
running: false,
watcher, watcher,
last_update_req: None, last_update_req: None,
shared, shared,
@ -171,19 +186,21 @@ impl CheckWatcherState {
} }
pub fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) { pub fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
self.running = true; loop {
while self.running {
select! { select! {
recv(&cmd_recv) -> cmd => match cmd { recv(&cmd_recv) -> cmd => match cmd {
Ok(cmd) => self.handle_command(cmd), Ok(cmd) => self.handle_command(cmd),
Err(RecvError) => { Err(RecvError) => {
// Command channel has closed, so shut down // Command channel has closed, so shut down
self.running = false; break;
}, },
}, },
recv(self.watcher.message_recv) -> msg => match msg { recv(self.watcher.message_recv) -> msg => match msg {
Ok(msg) => self.handle_message(msg, task_send), Ok(msg) => self.handle_message(msg, task_send),
Err(RecvError) => {}, Err(RecvError) => {
// Task channel has closed, so shut down
break;
},
} }
}; };