Skip to content

Commit

Permalink
simplify build loop, fixing target#212
Browse files Browse the repository at this point in the history
  • Loading branch information
curiousleo committed Nov 13, 2019
1 parent bb8a6ce commit 831bac7
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 394 deletions.
75 changes: 47 additions & 28 deletions src/build_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::pathreduction::reduce_paths;
use crate::project::roots;
use crate::project::roots::Roots;
use crate::project::Project;
use crate::watch::{DebugMessage, RawEventError, Reason, Watch};
use crate::watch::{DebugMessage, EventError, Reason, Watch};
use crossbeam_channel as chan;
use std::path::PathBuf;

Expand Down Expand Up @@ -63,43 +63,62 @@ impl<'a> BuildLoop<'a> {
/// Sends `Event`s over `Self.tx` once they happen.
/// When new filesystem changes are detected while a build is
/// still running, it is finished first before starting a new build.
pub fn forever(&mut self, tx: chan::Sender<Event>) {
pub fn forever(&mut self, tx: chan::Sender<Event>, rx_ping: chan::Receiver<()>) {
let send = |msg| tx.send(msg).expect("Failed to send an event");
let translate_reason = |rsn| match rsn {
Ok(rsn) => rsn,
// we should continue and just cite an unknown reason
Err(EventError::EventHasNoFilePath(msg)) => {
warn!(
"Event has no file path; possible issue with the watcher?: {:#?}",
msg
);
// can’t Clone `Event`s, so we return the Debug output here
Reason::UnknownEvent(DebugMessage::from(format!("{:#?}", msg)))
}
Err(EventError::RxNoEventReceived) => {
panic!("The file watcher died!");
}
};

send(Event::Started(Reason::ProjectAdded(
// The project has just been added, so run the builder in the first iteration
let mut reason = Some(Event::Started(Reason::ProjectAdded(
self.project.nix_file.clone(),
)));

// Drain pings to avoid unnecessarily building the project multiple times
rx_ping.try_iter().for_each(drop);

let rx_notify_raw = self.watch.rx.clone();

loop {
match self.once() {
Ok(result) => send(Event::Completed(result)),
Err(BuildError::Recoverable(failure)) => send(Event::Failure(failure)),
Err(BuildError::Unrecoverable(err)) => {
panic!("Unrecoverable error:\n{:#?}", err);
if let Some(rsn) = reason {
send(rsn);
match self.once() {
Ok(result) => send(Event::Completed(result)),
Err(BuildError::Recoverable(failure)) => send(Event::Failure(failure)),
Err(BuildError::Unrecoverable(err)) => {
panic!("Unrecoverable error:\n{:#?}", err);
}
}
reason = None;
}

let reason = match self.watch.wait_for_change() {
Ok(r) => r,
// we should continue and just cite an unknown reason
Err(RawEventError::EventHasNoFilePath(msg)) => {
warn!(
"Event has no file path; possible issue with the watcher?: {:#?}",
msg
);
// can’t Clone RawEvents, so we return the Debug output here
Reason::UnknownEvent(DebugMessage::from(format!("{:#?}", msg)))
// Sadly, clippy does not realise that these unsafe operations are triggered by the
// macro and are in fact perfectly fine.
#[allow(clippy::drop_copy, clippy::zero_ptr)]
{
chan::select! {
recv(rx_notify_raw) -> msg => if let Ok(msg) = msg {
if let Some(rsn) = self.watch.process(msg) {
reason = Some(Event::Started(translate_reason(rsn)));
}
},
recv(rx_ping) -> msg => if let Ok(()) = msg {
reason = Some(Event::Started(Reason::PingReceived));
},
}
Err(RawEventError::RxNoEventReceived) => {
panic!("The file watcher died!");
}
};

// TODO: Make err use Display instead of Debug.
// Otherwise user errors (especially for IO errors)
// are pretty hard to debug. Might need to review
// whether we can handle some errors earlier than here.
send(Event::Started(reason));
}
}
}

Expand Down
25 changes: 18 additions & 7 deletions src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ pub struct IndicateActivity {
pub nix_file: NixFile,
}

struct Handler {
tx: chan::Sender<()>,
_handle: std::thread::JoinHandle<()>,
}

/// Keeps all state of the running `lorri daemon` service, watches nix files and runs builds.
pub struct Daemon {
/// A thread for each `BuildLoop`, keyed by the nix files listened on.
handler_threads: HashMap<NixFile, std::thread::JoinHandle<()>>,
handler_threads: HashMap<NixFile, Handler>,
/// Sending end that we pass to every `BuildLoop` the daemon controls.
// TODO: this needs to transmit information to identify the builder with
build_events_tx: chan::Sender<crate::build_loop::Event>,
Expand Down Expand Up @@ -64,19 +69,25 @@ impl Daemon {
/// Add nix file to the set of files this daemon watches
/// & build if they change.
pub fn add(&mut self, project: Project) {
let tx = self.build_events_tx.clone();
let (tx, rx) = chan::unbounded();
let build_events_tx = self.build_events_tx.clone();

self.handler_threads
.entry(project.nix_file.clone())
.or_insert_with(|| {
std::thread::spawn(move || {
.or_insert_with(|| Handler {
tx,
_handle: std::thread::spawn(move || {
let mut build_loop = BuildLoop::new(&project);

// cloning the tx means the daemon’s rx gets all
// messages from all builders.
build_loop.forever(tx);
})
});
build_loop.forever(build_events_tx, rx);
}),
})
// Notify the handler, whether or not it was newly added
.tx
.send(())
.unwrap();
}
}

Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ pub mod constants;
pub mod daemon;
pub mod locate_file;
pub mod logging;
pub mod mpsc;
pub mod nix;
pub mod ops;
pub mod osstrlines;
Expand Down
242 changes: 0 additions & 242 deletions src/mpsc/filter_timeout_iterator.rs

This file was deleted.

Loading

0 comments on commit 831bac7

Please sign in to comment.