diff options
author | Aleksey Kladov <[email protected]> | 2020-06-25 12:47:22 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-06-25 12:47:22 +0100 |
commit | 331addcf61f9e2e0d14541e066dacda453f8fb54 (patch) | |
tree | 44d476d918436ca6ecd92b67ae9141d2208b0d51 /crates/flycheck | |
parent | 193ea7cf9af8c501035445b42847b6e80b33751a (diff) |
Canonicalize actor API
Diffstat (limited to 'crates/flycheck')
-rw-r--r-- | crates/flycheck/src/lib.rs | 124 |
1 files changed, 55 insertions, 69 deletions
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 3e73cf6ff..9e8205ae7 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -10,7 +10,7 @@ use std::{ | |||
10 | time::Instant, | 10 | time::Instant, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
14 | 14 | ||
15 | pub use cargo_metadata::diagnostic::{ | 15 | pub use cargo_metadata::diagnostic::{ |
16 | Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, | 16 | Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, |
@@ -61,7 +61,7 @@ impl FlycheckHandle { | |||
61 | ) -> FlycheckHandle { | 61 | ) -> FlycheckHandle { |
62 | let (cmd_send, cmd_recv) = unbounded::<Restart>(); | 62 | let (cmd_send, cmd_recv) = unbounded::<Restart>(); |
63 | let handle = jod_thread::spawn(move || { | 63 | let handle = jod_thread::spawn(move || { |
64 | FlycheckActor::new(sender, config, workspace_root).run(&cmd_recv); | 64 | FlycheckActor::new(sender, config, workspace_root).run(cmd_recv); |
65 | }); | 65 | }); |
66 | FlycheckHandle { cmd_send, handle } | 66 | FlycheckHandle { cmd_send, handle } |
67 | } | 67 | } |
@@ -98,14 +98,18 @@ struct FlycheckActor { | |||
98 | config: FlycheckConfig, | 98 | config: FlycheckConfig, |
99 | workspace_root: PathBuf, | 99 | workspace_root: PathBuf, |
100 | last_update_req: Option<Instant>, | 100 | last_update_req: Option<Instant>, |
101 | // XXX: drop order is significant | ||
102 | message_recv: Receiver<CheckEvent>, | ||
103 | /// WatchThread exists to wrap around the communication needed to be able to | 101 | /// WatchThread exists to wrap around the communication needed to be able to |
104 | /// run `cargo check` without blocking. Currently the Rust standard library | 102 | /// run `cargo check` without blocking. Currently the Rust standard library |
105 | /// doesn't provide a way to read sub-process output without blocking, so we | 103 | /// doesn't provide a way to read sub-process output without blocking, so we |
106 | /// have to wrap sub-processes output handling in a thread and pass messages | 104 | /// have to wrap sub-processes output handling in a thread and pass messages |
107 | /// back over a channel. | 105 | /// back over a channel. |
108 | check_process: Option<jod_thread::JoinHandle>, | 106 | // XXX: drop order is significant |
107 | check_process: Option<(Receiver<CheckEvent>, jod_thread::JoinHandle)>, | ||
108 | } | ||
109 | |||
110 | enum Event { | ||
111 | Restart(Restart), | ||
112 | CheckEvent(Option<CheckEvent>), | ||
109 | } | 113 | } |
110 | 114 | ||
111 | impl FlycheckActor { | 115 | impl FlycheckActor { |
@@ -114,40 +118,48 @@ impl FlycheckActor { | |||
114 | config: FlycheckConfig, | 118 | config: FlycheckConfig, |
115 | workspace_root: PathBuf, | 119 | workspace_root: PathBuf, |
116 | ) -> FlycheckActor { | 120 | ) -> FlycheckActor { |
117 | FlycheckActor { | 121 | FlycheckActor { sender, config, workspace_root, last_update_req: None, check_process: None } |
118 | sender, | ||
119 | config, | ||
120 | workspace_root, | ||
121 | last_update_req: None, | ||
122 | message_recv: never(), | ||
123 | check_process: None, | ||
124 | } | ||
125 | } | 122 | } |
126 | 123 | ||
127 | fn run(&mut self, cmd_recv: &Receiver<Restart>) { | 124 | fn run(&mut self, inbox: Receiver<Restart>) { |
128 | // If we rerun the thread, we need to discard the previous check results first | 125 | // If we rerun the thread, we need to discard the previous check results first |
129 | self.clean_previous_results(); | 126 | self.send(Message::ClearDiagnostics); |
130 | 127 | self.send(Message::Progress(Progress::End)); | |
131 | loop { | 128 | |
132 | select! { | 129 | while let Some(event) = self.next_event(&inbox) { |
133 | recv(&cmd_recv) -> cmd => match cmd { | 130 | match event { |
134 | Ok(Restart) => self.last_update_req = Some(Instant::now()), | 131 | Event::Restart(Restart) => self.last_update_req = Some(Instant::now()), |
135 | Err(RecvError) => { | 132 | Event::CheckEvent(None) => { |
136 | // Command channel has closed, so shut down | 133 | // Watcher finished, replace it with a never channel to |
137 | break; | 134 | // avoid busy-waiting. |
138 | }, | 135 | self.check_process = None; |
139 | }, | ||
140 | recv(self.message_recv) -> msg => match msg { | ||
141 | Ok(msg) => self.handle_message(msg), | ||
142 | Err(RecvError) => { | ||
143 | // Watcher finished, replace it with a never channel to | ||
144 | // avoid busy-waiting. | ||
145 | self.message_recv = never(); | ||
146 | self.check_process = None; | ||
147 | }, | ||
148 | } | 136 | } |
149 | }; | 137 | Event::CheckEvent(Some(event)) => match event { |
138 | CheckEvent::Begin => { | ||
139 | self.send(Message::Progress(Progress::Being)); | ||
140 | } | ||
141 | |||
142 | CheckEvent::End => { | ||
143 | self.send(Message::Progress(Progress::End)); | ||
144 | } | ||
145 | |||
146 | CheckEvent::Msg(cargo_metadata::Message::CompilerArtifact(msg)) => { | ||
147 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); | ||
148 | } | ||
149 | |||
150 | CheckEvent::Msg(cargo_metadata::Message::CompilerMessage(msg)) => { | ||
151 | self.send(Message::AddDiagnostic { | ||
152 | workspace_root: self.workspace_root.clone(), | ||
153 | diagnostic: msg.message, | ||
154 | }); | ||
155 | } | ||
150 | 156 | ||
157 | CheckEvent::Msg(cargo_metadata::Message::BuildScriptExecuted(_)) | ||
158 | | CheckEvent::Msg(cargo_metadata::Message::BuildFinished(_)) | ||
159 | | CheckEvent::Msg(cargo_metadata::Message::TextLine(_)) | ||
160 | | CheckEvent::Msg(cargo_metadata::Message::Unknown) => {} | ||
161 | }, | ||
162 | } | ||
151 | if self.should_recheck() { | 163 | if self.should_recheck() { |
152 | self.last_update_req = None; | 164 | self.last_update_req = None; |
153 | self.send(Message::ClearDiagnostics); | 165 | self.send(Message::ClearDiagnostics); |
@@ -156,9 +168,12 @@ impl FlycheckActor { | |||
156 | } | 168 | } |
157 | } | 169 | } |
158 | 170 | ||
159 | fn clean_previous_results(&self) { | 171 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { |
160 | self.send(Message::ClearDiagnostics); | 172 | let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); |
161 | self.send(Message::Progress(Progress::End)); | 173 | select! { |
174 | recv(inbox) -> msg => msg.ok().map(Event::Restart), | ||
175 | recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), | ||
176 | } | ||
162 | } | 177 | } |
163 | 178 | ||
164 | fn should_recheck(&mut self) -> bool { | 179 | fn should_recheck(&mut self) -> bool { |
@@ -171,37 +186,8 @@ impl FlycheckActor { | |||
171 | false | 186 | false |
172 | } | 187 | } |
173 | 188 | ||
174 | fn handle_message(&self, msg: CheckEvent) { | ||
175 | match msg { | ||
176 | CheckEvent::Begin => { | ||
177 | self.send(Message::Progress(Progress::Being)); | ||
178 | } | ||
179 | |||
180 | CheckEvent::End => { | ||
181 | self.send(Message::Progress(Progress::End)); | ||
182 | } | ||
183 | |||
184 | CheckEvent::Msg(cargo_metadata::Message::CompilerArtifact(msg)) => { | ||
185 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); | ||
186 | } | ||
187 | |||
188 | CheckEvent::Msg(cargo_metadata::Message::CompilerMessage(msg)) => { | ||
189 | self.send(Message::AddDiagnostic { | ||
190 | workspace_root: self.workspace_root.clone(), | ||
191 | diagnostic: msg.message, | ||
192 | }); | ||
193 | } | ||
194 | |||
195 | CheckEvent::Msg(cargo_metadata::Message::BuildScriptExecuted(_)) | ||
196 | | CheckEvent::Msg(cargo_metadata::Message::BuildFinished(_)) | ||
197 | | CheckEvent::Msg(cargo_metadata::Message::TextLine(_)) | ||
198 | | CheckEvent::Msg(cargo_metadata::Message::Unknown) => {} | ||
199 | } | ||
200 | } | ||
201 | |||
202 | fn restart_check_process(&mut self) { | 189 | fn restart_check_process(&mut self) { |
203 | // First, clear and cancel the old thread | 190 | // First, clear and cancel the old thread |
204 | self.message_recv = never(); | ||
205 | self.check_process = None; | 191 | self.check_process = None; |
206 | 192 | ||
207 | let mut cmd = match &self.config { | 193 | let mut cmd = match &self.config { |
@@ -237,8 +223,7 @@ impl FlycheckActor { | |||
237 | cmd.current_dir(&self.workspace_root); | 223 | cmd.current_dir(&self.workspace_root); |
238 | 224 | ||
239 | let (message_send, message_recv) = unbounded(); | 225 | let (message_send, message_recv) = unbounded(); |
240 | self.message_recv = message_recv; | 226 | let thread = jod_thread::spawn(move || { |
241 | self.check_process = Some(jod_thread::spawn(move || { | ||
242 | // If we trigger an error here, we will do so in the loop instead, | 227 | // If we trigger an error here, we will do so in the loop instead, |
243 | // which will break out of the loop, and continue the shutdown | 228 | // which will break out of the loop, and continue the shutdown |
244 | let _ = message_send.send(CheckEvent::Begin); | 229 | let _ = message_send.send(CheckEvent::Begin); |
@@ -267,7 +252,8 @@ impl FlycheckActor { | |||
267 | // We can ignore any error here, as we are already in the progress | 252 | // We can ignore any error here, as we are already in the progress |
268 | // of shutting down. | 253 | // of shutting down. |
269 | let _ = message_send.send(CheckEvent::End); | 254 | let _ = message_send.send(CheckEvent::End); |
270 | })) | 255 | }); |
256 | self.check_process = Some((message_recv, thread)) | ||
271 | } | 257 | } |
272 | 258 | ||
273 | fn send(&self, check_task: Message) { | 259 | fn send(&self, check_task: Message) { |