Skip to content

Commit

Permalink
Wait for child processes to finish (#345)
Browse files Browse the repository at this point in the history
Thanks to @bheisler for the feature request and initial implementation.

Fixes #302
  • Loading branch information
casey authored Aug 27, 2018
1 parent c615a3f commit b14d1ec
Show file tree
Hide file tree
Showing 16 changed files with 465 additions and 80 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cargo.lock linguist-generated diff=nodiff
81 changes: 81 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ brev = "0.1.6"
clap = "2.0.0"
dotenv = "0.13.0"
edit-distance = "2.0.0"
env_logger = "0.5.12"
itertools = "0.7"
lazy_static = "1.0.0"
libc = "0.2.21"
log = "0.4.4"
regex = "1.0.0"
target = "1.0.0"
tempdir = "0.3.5"
unicode-width = "0.1.3"

[dependencies.ctrlc]
version = "3.1"
features = ['termination']

[dev-dependencies]
executable-path = "1.0.0"
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ check:
cargo check

watch COMMAND='test':
cargo watch {{COMMAND}}
cargo watch --clear --exec {{COMMAND}}

version = `sed -En 's/version[[:space:]]*=[[:space:]]*"([^"]+)"/v\1/p' Cargo.toml`

Expand Down
2 changes: 2 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tab_spaces = 2
max_width = 100
5 changes: 2 additions & 3 deletions src/assignment_evaluator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::path::PathBuf;

use common::*;

use brev;
Expand Down Expand Up @@ -154,8 +152,9 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
process::Stdio::inherit()
});

brev::output(cmd)
InterruptHandler::guard(|| brev::output(cmd)
.map_err(|output_error| RuntimeError::Backtick{token: token.clone(), output_error})
)
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use std::io::prelude::*;
pub use std::ops::Range;
pub use std::path::{Path, PathBuf};
pub use std::process::Command;
pub use std::sync::{Mutex, MutexGuard};
pub use std::{cmp, env, fs, fmt, io, iter, process, vec, usize};

pub use color::Color;
Expand All @@ -21,6 +22,7 @@ pub use cooked_string::CookedString;
pub use expression::Expression;
pub use fragment::Fragment;
pub use function::{evaluate_function, resolve_function, FunctionContext};
pub use interrupt_handler::InterruptHandler;
pub use justfile::Justfile;
pub use lexer::Lexer;
pub use load_dotenv::load_dotenv;
Expand Down
7 changes: 7 additions & 0 deletions src/die.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
macro_rules! die {
($($arg:tt)*) => {{
extern crate std;
eprintln!($($arg)*);
process::exit(EXIT_FAILURE)
}};
}
9 changes: 4 additions & 5 deletions src/function.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::path::PathBuf;

use common::*;

use target;

use platform::{Platform, PlatformInterface};
Expand Down Expand Up @@ -106,14 +105,14 @@ pub fn invocation_directory(context: &FunctionContext) -> Result<String, String>

pub fn env_var(context: &FunctionContext, key: &str) -> Result<String, String> {
use std::env::VarError::*;

if let Some(value) = context.dotenv.get(key) {
return Ok(value.clone());
}

match env::var(key) {
Err(NotPresent) => Err(format!("environment variable `{}` not present", key)),
Err(NotUnicode(os_string)) =>
Err(NotUnicode(os_string)) =>
Err(format!("environment variable `{}` not unicode: {:?}", key, os_string)),
Ok(value) => Ok(value),
}
Expand All @@ -131,7 +130,7 @@ pub fn env_var_or_default(
use std::env::VarError::*;
match env::var(key) {
Err(NotPresent) => Ok(default.to_string()),
Err(NotUnicode(os_string)) =>
Err(NotUnicode(os_string)) =>
Err(format!("environment variable `{}` not unicode: {:?}", key, os_string)),
Ok(value) => Ok(value),
}
Expand Down
93 changes: 93 additions & 0 deletions src/interrupt_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use common::*;

use ctrlc;

pub struct InterruptHandler {
blocks: u32,
interrupted: bool,
}

impl InterruptHandler {
pub fn install() -> Result<(), ctrlc::Error> {
ctrlc::set_handler(|| InterruptHandler::instance().interrupt())
}

fn instance() -> MutexGuard<'static, InterruptHandler> {
lazy_static! {
static ref INSTANCE: Mutex<InterruptHandler> = Mutex::new(InterruptHandler::new());
}

match INSTANCE.lock() {
Ok(guard) => guard,
Err(poison_error) => die!(
"{}",
RuntimeError::Internal {
message: format!("interrupt handler mutex poisoned: {}", poison_error),
}
),
}
}

fn new() -> InterruptHandler {
InterruptHandler {
blocks: 0,
interrupted: false,
}
}

fn interrupt(&mut self) {
self.interrupted = true;

if self.blocks > 0 {
return;
}

Self::exit();
}

fn exit() {
process::exit(130);
}

fn block(&mut self) {
self.blocks += 1;
}

fn unblock(&mut self) {
if self.blocks == 0 {
die!(
"{}",
RuntimeError::Internal {
message: "attempted to unblock interrupt handler, but handler was not blocked"
.to_string(),
}
);
}

self.blocks -= 1;

if self.interrupted {
Self::exit();
}
}

pub fn guard<T, F: FnOnce() -> T>(function: F) -> T {
let _guard = InterruptGuard::new();
function()
}
}

pub struct InterruptGuard;

impl InterruptGuard {
fn new() -> InterruptGuard {
InterruptHandler::instance().block();
InterruptGuard
}
}

impl Drop for InterruptGuard {
fn drop(&mut self) {
InterruptHandler::instance().unblock();
}
}
Loading

0 comments on commit b14d1ec

Please sign in to comment.