]> git.lizzy.rs Git - rust.git/commitdiff
Shutdown/cancelation story for main cargo watch thread
authorEmil Lauridsen <mine809@gmail.com>
Fri, 27 Dec 2019 10:43:05 +0000 (11:43 +0100)
committerEmil Lauridsen <mine809@gmail.com>
Fri, 27 Dec 2019 10:43:05 +0000 (11:43 +0100)
crates/ra_cargo_watch/src/lib.rs

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