Skip to content

Commit 8e63879

Browse files
erickt0xpr03
authored andcommitted
fsevent should join on thread shutdown
We had some test failures because crossbeam-channel may panic when trying to call recv() during thread shutdown. This seems to be similar to this upstream bug: crossbeam-rs/crossbeam#321. Unfortunately it seems that some operating systems may tear down thread-local storage early, rust-lang/rust#28129, which can trigger panics if trying to interact with TLS during a drop. To avoid this issue, this switches from using a channel to signal the thread shutdown to just using the join handle (which we should have been doing anyway).
1 parent 57daf85 commit 8e63879

File tree

1 file changed

+7
-17
lines changed

1 file changed

+7
-17
lines changed

src/fsevent.rs

+7-17
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
use crate::event::*;
1818
use crate::{Config, Error, EventFn, RecursiveMode, Result, Watcher};
19-
use crossbeam_channel::{unbounded, Receiver, Sender};
19+
use crossbeam_channel::{unbounded, Sender};
2020
use fsevent_sys as fs;
2121
use fsevent_sys::core_foundation as cf;
2222
use std::collections::HashMap;
@@ -64,7 +64,7 @@ pub struct FsEventWatcher {
6464
latency: cf::CFTimeInterval,
6565
flags: fs::FSEventStreamCreateFlags,
6666
event_fn: Arc<Mutex<dyn EventFn>>,
67-
runloop: Option<(cf::CFRunLoopRef, Receiver<()>)>,
67+
runloop: Option<(cf::CFRunLoopRef, thread::JoinHandle<()>)>,
6868
recursive_info: HashMap<PathBuf, bool>,
6969
}
7070

@@ -294,7 +294,7 @@ impl FsEventWatcher {
294294
return;
295295
}
296296

297-
if let Some((runloop, done)) = self.runloop.take() {
297+
if let Some((runloop, thread_handle)) = self.runloop.take() {
298298
unsafe {
299299
let runloop = runloop as *mut raw::c_void;
300300

@@ -305,11 +305,8 @@ impl FsEventWatcher {
305305
cf::CFRunLoopStop(runloop);
306306
}
307307

308-
// sync done channel
309-
match done.recv() {
310-
Ok(()) => (),
311-
Err(_) => panic!("the runloop may not be finished!"),
312-
}
308+
// Wait for the thread to shut down.
309+
thread_handle.join().expect("thread to shut down");
313310
}
314311
}
315312

@@ -385,9 +382,6 @@ impl FsEventWatcher {
385382
return Err(Error::path_not_found());
386383
}
387384

388-
// done channel is used to sync quit status of runloop thread
389-
let (done_tx, done_rx) = unbounded();
390-
391385
// We need to associate the stream context with our callback in order to propagate events
392386
// to the rest of the system. This will be owned by the stream, and will be freed when the
393387
// stream is closed. This means we will leak the context if we panic before reacing
@@ -431,7 +425,7 @@ impl FsEventWatcher {
431425
// channel to pass runloop around
432426
let (rl_tx, rl_rx) = unbounded();
433427

434-
thread::spawn(move || {
428+
let thread_handle = thread::spawn(move || {
435429
let stream = stream.0;
436430

437431
unsafe {
@@ -454,13 +448,9 @@ impl FsEventWatcher {
454448
fs::FSEventStreamInvalidate(stream);
455449
fs::FSEventStreamRelease(stream);
456450
}
457-
458-
done_tx
459-
.send(())
460-
.expect("error while signal run loop is done");
461451
});
462452
// block until runloop has been sent
463-
self.runloop = Some((rl_rx.recv().unwrap().0, done_rx));
453+
self.runloop = Some((rl_rx.recv().unwrap().0, thread_handle));
464454

465455
Ok(())
466456
}

0 commit comments

Comments
 (0)