@@ -79,17 +79,25 @@ pub enum CheckCommand {
7979struct CheckWatcherThread {
8080 options : CheckConfig ,
8181 workspace_root : PathBuf ,
82- watcher : WatchThread ,
8382 last_update_req : Option < Instant > ,
83+ // XXX: drop order is significant
84+ message_recv : Receiver < CheckEvent > ,
85+ /// WatchThread exists to wrap around the communication needed to be able to
86+ /// run `cargo check` without blocking. Currently the Rust standard library
87+ /// doesn't provide a way to read sub-process output without blocking, so we
88+ /// have to wrap sub-processes output handling in a thread and pass messages
89+ /// back over a channel.
90+ check_process : Option < jod_thread:: JoinHandle < ( ) > > ,
8491}
8592
8693impl CheckWatcherThread {
8794 fn new ( options : CheckConfig , workspace_root : PathBuf ) -> CheckWatcherThread {
8895 CheckWatcherThread {
8996 options,
9097 workspace_root,
91- watcher : WatchThread :: dummy ( ) ,
9298 last_update_req : None ,
99+ message_recv : never ( ) ,
100+ check_process : None ,
93101 }
94102 }
95103
@@ -106,25 +114,21 @@ impl CheckWatcherThread {
106114 break ;
107115 } ,
108116 } ,
109- recv( self . watcher . message_recv) -> msg => match msg {
117+ recv( self . message_recv) -> msg => match msg {
110118 Ok ( msg) => self . handle_message( msg, task_send) ,
111119 Err ( RecvError ) => {
112120 // Watcher finished, replace it with a never channel to
113121 // avoid busy-waiting.
114- std:: mem:: replace( & mut self . watcher. message_recv, never( ) ) ;
122+ self . message_recv = never( ) ;
123+ self . check_process = None ;
115124 } ,
116125 }
117126 } ;
118127
119128 if self . should_recheck ( ) {
120- self . last_update_req . take ( ) ;
129+ self . last_update_req = None ;
121130 task_send. send ( CheckTask :: ClearDiagnostics ) . unwrap ( ) ;
122-
123- // Replace with a dummy watcher first so we drop the original and wait for completion
124- std:: mem:: replace ( & mut self . watcher , WatchThread :: dummy ( ) ) ;
125-
126- // Then create the actual new watcher
127- self . watcher = WatchThread :: new ( & self . options , & self . workspace_root ) ;
131+ self . restart_check_process ( ) ;
128132 }
129133 }
130134 }
@@ -207,6 +211,59 @@ impl CheckWatcherThread {
207211 CheckEvent :: Msg ( Message :: Unknown ) => { }
208212 }
209213 }
214+
215+ fn restart_check_process ( & mut self ) {
216+ // First, clear and cancel the old thread
217+ self . message_recv = never ( ) ;
218+ self . check_process = None ;
219+ if !self . options . enable {
220+ return ;
221+ }
222+
223+ let mut args: Vec < String > = vec ! [
224+ self . options. command. clone( ) ,
225+ "--workspace" . to_string( ) ,
226+ "--message-format=json" . to_string( ) ,
227+ "--manifest-path" . to_string( ) ,
228+ format!( "{}/Cargo.toml" , self . workspace_root. display( ) ) ,
229+ ] ;
230+ if self . options . all_targets {
231+ args. push ( "--all-targets" . to_string ( ) ) ;
232+ }
233+ args. extend ( self . options . args . iter ( ) . cloned ( ) ) ;
234+
235+ let ( message_send, message_recv) = unbounded ( ) ;
236+ let workspace_root = self . workspace_root . to_owned ( ) ;
237+ self . message_recv = message_recv;
238+ self . check_process = Some ( jod_thread:: spawn ( move || {
239+ // If we trigger an error here, we will do so in the loop instead,
240+ // which will break out of the loop, and continue the shutdown
241+ let _ = message_send. send ( CheckEvent :: Begin ) ;
242+
243+ let res = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
244+ // Skip certain kinds of messages to only spend time on what's useful
245+ match & message {
246+ Message :: CompilerArtifact ( artifact) if artifact. fresh => return true ,
247+ Message :: BuildScriptExecuted ( _) => return true ,
248+ Message :: Unknown => return true ,
249+ _ => { }
250+ }
251+
252+ // if the send channel was closed, we want to shutdown
253+ message_send. send ( CheckEvent :: Msg ( message) ) . is_ok ( )
254+ } ) ;
255+
256+ if let Err ( err) = res {
257+ // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
258+ // to display user-caused misconfiguration errors instead of just logging them here
259+ log:: error!( "Cargo watcher failed {:?}" , err) ;
260+ }
261+
262+ // We can ignore any error here, as we are already in the progress
263+ // of shutting down.
264+ let _ = message_send. send ( CheckEvent :: End ) ;
265+ } ) )
266+ }
210267}
211268
212269#[ derive( Debug ) ]
@@ -215,19 +272,6 @@ pub struct DiagnosticWithFixes {
215272 fixes : Vec < CodeAction > ,
216273}
217274
218- /// WatchThread exists to wrap around the communication needed to be able to
219- /// run `cargo check` without blocking. Currently the Rust standard library
220- /// doesn't provide a way to read sub-process output without blocking, so we
221- /// have to wrap sub-processes output handling in a thread and pass messages
222- /// back over a channel.
223- /// The correct way to dispose of the thread is to drop it, on which the
224- /// sub-process will be killed, and the thread will be joined.
225- struct WatchThread {
226- // XXX: drop order is significant
227- message_recv : Receiver < CheckEvent > ,
228- _handle : Option < jod_thread:: JoinHandle < ( ) > > ,
229- }
230-
231275enum CheckEvent {
232276 Begin ,
233277 Msg ( cargo_metadata:: Message ) ,
@@ -317,59 +361,3 @@ fn run_cargo(
317361
318362 Err ( CargoError ( err_msg) )
319363}
320-
321- impl WatchThread {
322- fn dummy ( ) -> WatchThread {
323- WatchThread { message_recv : never ( ) , _handle : None }
324- }
325-
326- fn new ( options : & CheckConfig , workspace_root : & Path ) -> WatchThread {
327- let mut args: Vec < String > = vec ! [
328- options. command. clone( ) ,
329- "--workspace" . to_string( ) ,
330- "--message-format=json" . to_string( ) ,
331- "--manifest-path" . to_string( ) ,
332- format!( "{}/Cargo.toml" , workspace_root. display( ) ) ,
333- ] ;
334- if options. all_targets {
335- args. push ( "--all-targets" . to_string ( ) ) ;
336- }
337- args. extend ( options. args . iter ( ) . cloned ( ) ) ;
338-
339- let ( message_send, message_recv) = unbounded ( ) ;
340- let workspace_root = workspace_root. to_owned ( ) ;
341- let handle = if options. enable {
342- Some ( jod_thread:: spawn ( move || {
343- // If we trigger an error here, we will do so in the loop instead,
344- // which will break out of the loop, and continue the shutdown
345- let _ = message_send. send ( CheckEvent :: Begin ) ;
346-
347- let res = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
348- // Skip certain kinds of messages to only spend time on what's useful
349- match & message {
350- Message :: CompilerArtifact ( artifact) if artifact. fresh => return true ,
351- Message :: BuildScriptExecuted ( _) => return true ,
352- Message :: Unknown => return true ,
353- _ => { }
354- }
355-
356- // if the send channel was closed, we want to shutdown
357- message_send. send ( CheckEvent :: Msg ( message) ) . is_ok ( )
358- } ) ;
359-
360- if let Err ( err) = res {
361- // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
362- // to display user-caused misconfiguration errors instead of just logging them here
363- log:: error!( "Cargo watcher failed {:?}" , err) ;
364- }
365-
366- // We can ignore any error here, as we are already in the progress
367- // of shutting down.
368- let _ = message_send. send ( CheckEvent :: End ) ;
369- } ) )
370- } else {
371- None
372- } ;
373- WatchThread { message_recv, _handle : handle }
374- }
375- }
0 commit comments