@@ -85,7 +85,7 @@ pub enum Message {
8585pub enum Progress {
8686 DidStart ,
8787 DidCheckCrate ( String ) ,
88- DidFinish ,
88+ DidFinish ( io :: Result < ( ) > ) ,
8989 DidCancel ,
9090}
9191
@@ -100,7 +100,7 @@ struct FlycheckActor {
100100 /// doesn't provide a way to read sub-process output without blocking, so we
101101 /// have to wrap sub-processes output handling in a thread and pass messages
102102 /// back over a channel.
103- check_process : Option < CargoHandle > ,
103+ cargo_handle : Option < CargoHandle > ,
104104}
105105
106106enum Event {
@@ -114,10 +114,10 @@ impl FlycheckActor {
114114 config : FlycheckConfig ,
115115 workspace_root : PathBuf ,
116116 ) -> FlycheckActor {
117- FlycheckActor { sender, config, workspace_root, check_process : None }
117+ FlycheckActor { sender, config, workspace_root, cargo_handle : None }
118118 }
119119 fn next_event ( & self , inbox : & Receiver < Restart > ) -> Option < Event > {
120- let check_chan = self . check_process . as_ref ( ) . map ( |cargo| & cargo. receiver ) ;
120+ let check_chan = self . cargo_handle . as_ref ( ) . map ( |cargo| & cargo. receiver ) ;
121121 select ! {
122122 recv( inbox) -> msg => msg. ok( ) . map( Event :: Restart ) ,
123123 recv( check_chan. unwrap_or( & never( ) ) ) -> msg => Some ( Event :: CheckEvent ( msg. ok( ) ) ) ,
@@ -128,15 +128,22 @@ impl FlycheckActor {
128128 match event {
129129 Event :: Restart ( Restart ) => {
130130 while let Ok ( Restart ) = inbox. recv_timeout ( Duration :: from_millis ( 50 ) ) { }
131+
131132 self . cancel_check_process ( ) ;
132- self . check_process = Some ( self . start_check_process ( ) ) ;
133- self . send ( Message :: Progress ( Progress :: DidStart ) ) ;
133+
134+ let mut command = self . check_command ( ) ;
135+ command. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: null ( ) ) . stdin ( Stdio :: null ( ) ) ;
136+ if let Ok ( child) = command. spawn ( ) . map ( JodChild ) {
137+ self . cargo_handle = Some ( CargoHandle :: spawn ( child) ) ;
138+ self . send ( Message :: Progress ( Progress :: DidStart ) ) ;
139+ }
134140 }
135141 Event :: CheckEvent ( None ) => {
136142 // Watcher finished, replace it with a never channel to
137143 // avoid busy-waiting.
138- assert ! ( self . check_process. take( ) . is_some( ) ) ;
139- self . send ( Message :: Progress ( Progress :: DidFinish ) ) ;
144+ let cargo_handle = self . cargo_handle . take ( ) . unwrap ( ) ;
145+ let res = cargo_handle. join ( ) ;
146+ self . send ( Message :: Progress ( Progress :: DidFinish ( res) ) ) ;
140147 }
141148 Event :: CheckEvent ( Some ( message) ) => match message {
142149 cargo_metadata:: Message :: CompilerArtifact ( msg) => {
@@ -161,11 +168,11 @@ impl FlycheckActor {
161168 self . cancel_check_process ( ) ;
162169 }
163170 fn cancel_check_process ( & mut self ) {
164- if self . check_process . take ( ) . is_some ( ) {
171+ if self . cargo_handle . take ( ) . is_some ( ) {
165172 self . send ( Message :: Progress ( Progress :: DidCancel ) ) ;
166173 }
167174 }
168- fn start_check_process ( & self ) -> CargoHandle {
175+ fn check_command ( & self ) -> Command {
169176 let mut cmd = match & self . config {
170177 FlycheckConfig :: CargoCommand {
171178 command,
@@ -197,8 +204,7 @@ impl FlycheckActor {
197204 }
198205 } ;
199206 cmd. current_dir ( & self . workspace_root ) ;
200-
201- CargoHandle :: spawn ( cmd)
207+ cmd
202208 }
203209
204210 fn send ( & self , check_task : Message ) {
@@ -207,49 +213,62 @@ impl FlycheckActor {
207213}
208214
209215struct CargoHandle {
210- receiver : Receiver < cargo_metadata :: Message > ,
216+ child : JodChild ,
211217 #[ allow( unused) ]
212- thread : jod_thread:: JoinHandle ,
218+ thread : jod_thread:: JoinHandle < io:: Result < bool > > ,
219+ receiver : Receiver < cargo_metadata:: Message > ,
213220}
214221
215222impl CargoHandle {
216- fn spawn ( command : Command ) -> CargoHandle {
223+ fn spawn ( mut child : JodChild ) -> CargoHandle {
224+ let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
217225 let ( sender, receiver) = unbounded ( ) ;
218- let actor = CargoActor :: new ( command, sender) ;
219- let thread = jod_thread:: spawn ( move || {
220- let _ = actor. run ( ) ;
221- } ) ;
222- CargoHandle { receiver, thread }
226+ let actor = CargoActor :: new ( child_stdout, sender) ;
227+ let thread = jod_thread:: spawn ( move || actor. run ( ) ) ;
228+ CargoHandle { child, thread, receiver }
229+ }
230+ fn join ( mut self ) -> io:: Result < ( ) > {
231+ // It is okay to ignore the result, as it only errors if the process is already dead
232+ let _ = self . child . kill ( ) ;
233+ let exit_status = self . child . wait ( ) ?;
234+ let read_at_least_one_message = self . thread . join ( ) ?;
235+ if !exit_status. success ( ) && !read_at_least_one_message {
236+ // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
237+ // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
238+ return Err ( io:: Error :: new (
239+ io:: ErrorKind :: Other ,
240+ format ! (
241+ "Cargo watcher failed,the command produced no valid metadata (exit code: {:?})" ,
242+ exit_status
243+ ) ,
244+ ) ) ;
245+ }
246+ Ok ( ( ) )
223247 }
224248}
225249
226250struct CargoActor {
227- command : Command ,
251+ child_stdout : process :: ChildStdout ,
228252 sender : Sender < cargo_metadata:: Message > ,
229253}
230254
231255impl CargoActor {
232- fn new ( command : Command , sender : Sender < cargo_metadata:: Message > ) -> CargoActor {
233- CargoActor { command, sender }
256+ fn new (
257+ child_stdout : process:: ChildStdout ,
258+ sender : Sender < cargo_metadata:: Message > ,
259+ ) -> CargoActor {
260+ CargoActor { child_stdout, sender }
234261 }
235- fn run ( mut self ) -> io:: Result < ( ) > {
236- let child = self
237- . command
238- . stdout ( Stdio :: piped ( ) )
239- . stderr ( Stdio :: null ( ) )
240- . stdin ( Stdio :: null ( ) )
241- . spawn ( ) ?;
242- let mut child = ChildKiller ( child) ;
243-
262+ fn run ( self ) -> io:: Result < bool > {
244263 // We manually read a line at a time, instead of using serde's
245264 // stream deserializers, because the deserializer cannot recover
246265 // from an error, resulting in it getting stuck, because we try to
247- // be resillient against failures.
266+ // be resilient against failures.
248267 //
249268 // Because cargo only outputs one JSON object per line, we can
250269 // simply skip a line if it doesn't parse, which just ignores any
251270 // erroneus output.
252- let stdout = BufReader :: new ( child . stdout . take ( ) . unwrap ( ) ) ;
271+ let stdout = BufReader :: new ( self . child_stdout ) ;
253272 let mut read_at_least_one_message = false ;
254273 for message in cargo_metadata:: Message :: parse_stream ( stdout) {
255274 let message = match message {
@@ -264,50 +283,32 @@ impl CargoActor {
264283
265284 // Skip certain kinds of messages to only spend time on what's useful
266285 match & message {
267- cargo_metadata:: Message :: CompilerArtifact ( artifact) if artifact. fresh => continue ,
286+ cargo_metadata:: Message :: CompilerArtifact ( artifact) if artifact. fresh => ( ) ,
268287 cargo_metadata:: Message :: BuildScriptExecuted ( _)
269- | cargo_metadata:: Message :: Unknown => continue ,
270- _ => {
271- // if the send channel was closed, we want to shutdown
272- if self . sender . send ( message) . is_err ( ) {
273- break ;
274- }
275- }
288+ | cargo_metadata:: Message :: Unknown => ( ) ,
289+ _ => self . sender . send ( message) . unwrap ( ) ,
276290 }
277291 }
278-
279- // It is okay to ignore the result, as it only errors if the process is already dead
280- let _ = child. kill ( ) ;
281-
282- let exit_status = child. wait ( ) ?;
283- if !exit_status. success ( ) && !read_at_least_one_message {
284- // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
285- // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
286-
287- // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
288- // to display user-caused misconfiguration errors instead of just logging them here
289- log:: error!( "Cargo watcher failed,the command produced no valid metadata (exit code: {:?}): {:?}" , exit_status, self . command) ;
290- }
291- Ok ( ( ) )
292+ Ok ( read_at_least_one_message)
292293 }
293294}
294295
295- struct ChildKiller ( process:: Child ) ;
296+ struct JodChild ( process:: Child ) ;
296297
297- impl ops:: Deref for ChildKiller {
298+ impl ops:: Deref for JodChild {
298299 type Target = process:: Child ;
299300 fn deref ( & self ) -> & process:: Child {
300301 & self . 0
301302 }
302303}
303304
304- impl ops:: DerefMut for ChildKiller {
305+ impl ops:: DerefMut for JodChild {
305306 fn deref_mut ( & mut self ) -> & mut process:: Child {
306307 & mut self . 0
307308 }
308309}
309310
310- impl Drop for ChildKiller {
311+ impl Drop for JodChild {
311312 fn drop ( & mut self ) {
312313 let _ = self . 0 . kill ( ) ;
313314 }
0 commit comments