Skip to content

Commit

Permalink
Don't block window thread when compiling model
Browse files Browse the repository at this point in the history
  • Loading branch information
hannobraun committed Oct 20, 2022
1 parent 7b17d01 commit edde0d7
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 54 deletions.
86 changes: 34 additions & 52 deletions crates/fj-host/src/watcher.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use std::{collections::HashSet, ffi::OsStr, thread};

use crossbeam_channel::{Receiver, Sender, TryRecvError};
use crossbeam_channel::Receiver;
use notify::Watcher as _;

use crate::{Error, LoadedShape, Model};

/// Watches a model for changes, reloading it continually
pub struct Watcher {
_watcher: Box<dyn notify::Watcher>,
channel: Receiver<()>,
model: Model,

event_tx: Sender<WatcherEvent>,
event_rx: Receiver<WatcherEvent>,
}

Expand Down Expand Up @@ -90,12 +86,41 @@ impl Watcher {
watch_tx_2.send(()).expect("Channel is disconnected")
});

// Listen on the watcher channel and rebuild the model. This happens in
// a separate thread from the watcher to allow us to trigger compiles
// without the watcher having registered a change, as is done above.
thread::spawn(move || loop {
let () = watch_rx
.recv()
.expect("Expected channel to never disconnect");

let shape = match model.load() {
Ok(shape) => shape,
Err(Error::Compile { output }) => {
event_tx
.send(WatcherEvent::StatusUpdate(format!(
"Failed to compile model:\n{}",
output
)))
.expect("Expected channel to never disconnect");

return;
}
Err(err) => {
event_tx
.send(WatcherEvent::Error(err))
.expect("Expected channel to never disconnect");
return;
}
};

event_tx
.send(WatcherEvent::Shape(shape))
.expect("Expected channel to never disconnect");
});

Ok(Self {
_watcher: Box::new(watcher),
channel: watch_rx,
model,

event_tx,
event_rx,
})
}
Expand All @@ -104,49 +129,6 @@ impl Watcher {
pub fn events(&self) -> Receiver<WatcherEvent> {
self.event_rx.clone()
}

/// Receive an updated shape that the reloaded model created
///
/// Returns `None`, if the model has not changed since the last time this
/// method was called.
pub fn receive_shape(&self) {
match self.channel.try_recv() {
Ok(()) => {
let shape = match self.model.load() {
Ok(shape) => shape,
Err(Error::Compile { output }) => {
self.event_tx
.send(WatcherEvent::StatusUpdate(format!(
"Failed to compile model:\n{}",
output
)))
.expect("Expected channel to never disconnect");

return;
}
Err(err) => {
self.event_tx
.send(WatcherEvent::Error(err))
.expect("Expected channel to never disconnect");
return;
}
};

self.event_tx
.send(WatcherEvent::Shape(shape))
.expect("Expected channel to never disconnect");
}
Err(TryRecvError::Empty) => {
// Nothing to receive from the channel.
}
Err(TryRecvError::Disconnected) => {
// The other end has disconnected. This is probably the result
// of a panic on the other thread, or a program shutdown in
// progress. In any case, not much we can do here.
panic!();
}
}
}
}

/// An event emitted by the [`Watcher`]
Expand Down
2 changes: 0 additions & 2 deletions crates/fj-window/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ pub fn run(
}
}

watcher.receive_shape();

if let Event::WindowEvent { event, .. } = &event {
// In theory we could/should check if `egui` wants "exclusive" use
// of this event here. But with the current integration with Fornjot
Expand Down

0 comments on commit edde0d7

Please sign in to comment.