[PATCH] Use signals to propagate failures
Export this patch
---
test.do fix kinda hacky but shouldn't matter in real usage
src/main.ha | 51 ++++++++++++++++++++++++++++++++++++++++-----------
test.do | 4 ++--
2 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/src/main.ha b/src/main.ha
index ad74a51..851acf6 100644
--- a/src/main.ha
+++ b/src/main.ha
@@ -14,6 +14,7 @@ use temp;
use time;
use types;
use unix;
+use unix::signal;
use uuid;
type context = struct {
@@ -21,15 +22,17 @@ type context = struct {
quiet: bool,
colorless: bool,
indent: str,
- toplevel: bool,
parent_timestamp: time::instant,
+ parent: (exec::process | void),
jobs: uint,
rd: io::file,
wr: io::file,
};
-let tmpdir: str = "";
-def OLD: time::instant = time::instant {
+let tmpdir = "";
+let failed = false;
+
+def OLD = time::instant {
sec = types::I64_MIN,
nsec = 0,
};
@@ -47,7 +50,13 @@ const envprogs: [_](str, str) = [
("SCDOC", "scdoc"),
];
+fn handler(sig: int, info: *signal::siginfo, ucontext: *void) void = {
+ failed = true;
+};
+
export fn main() void = {
+ signal::handle(signal::SIGUSR1, &handler);
+
// job slots for the -j option are handled by reading a byte from a pipe when
// a slot is acquired, and writing a byte to the pipe when a slot is freed.
let (rd, wr) = match (os::getenv("HAREDO_PIPE")) {
@@ -69,7 +78,11 @@ export fn main() void = {
quiet = os::getenv("HAREDO_QUIET") is str,
colorless = os::getenv("HAREDO_COLORLESS") is str,
indent = os::tryenv("HAREDO_INDENT", ""),
- toplevel = parent is void,
+ parent = match (os::getenv("HAREDO_PID")) {
+ case void => void;
+ case let s: str =>
+ yield strconv::stoi(s)!;
+ },
parent_timestamp = if (parent is void) {
yield OLD;
} else {
@@ -123,7 +136,7 @@ export fn main() void = {
free(children);
};
- if (ctx.toplevel) {
+ if (ctx.parent is void) {
// initialize job slots
for (let i = 0z; i < ctx.jobs - 1; i += 1) {
io::write(ctx.wr, [0])!;
@@ -141,13 +154,15 @@ export fn main() void = {
yield child;
case let e: exec::error =>
log(&ctx, lvl::ERR, cmd.args[i], exec::strerror(e))!;
- os::exit(1);
+ fail(&ctx);
};
if (ctx.jobs > 1) {
append(children, (child.0, child.1, cmd.args[i]));
} else {
// if there's only one job (or zero), wait immediately for the child
- const status = exec::wait(&child.0)!;
+ let status = exec::wait(&child.0);
+ for (status is errors::interrupted; status = exec::wait(&child.0)) void;
+ const status = status!;
defer io::write(ctx.wr, [0])!;
match (cleanup_child(&ctx, &status, child.1, cmd.args[i])) {
case let new: bool =>
@@ -155,14 +170,16 @@ export fn main() void = {
log(&ctx, lvl::OK, cmd.args[i], msg)!;
case let err: !str =>
log(&ctx, lvl::ERR, cmd.args[i], err)!;
- os::exit(1);
+ fail(&ctx);
};
};
};
// ...then wait for them to finish...
for (len(children) != 0) {
- const (child, status) = exec::waitany()!;
+ let w = exec::waitany();
+ for (w is errors::interrupted; w = exec::waitany()) void;
+ const (child, status) = w!;
let i = 0z;
for (i < len(children); i += 1) {
if (child == children[i].0) break;
@@ -179,10 +196,12 @@ export fn main() void = {
log(&ctx, lvl::OK, cmd.args[i], msg)!;
case let err: !str =>
log(&ctx, lvl::ERR, cmd.args[i], err)!;
- os::exit(1);
+ fail(&ctx);
};
};
+ if (failed) fail(&ctx);
+
// ...then check for updates
for (let i = 0z; i < len(cmd.args); i += 1) {
if (cmd.args[i] == "++") continue;
@@ -198,6 +217,15 @@ export fn main() void = {
os::exit(1);
};
+@noreturn fn fail(ctx: *context) void = {
+ match (ctx.parent) {
+ case void => void;
+ case let p: exec::process =>
+ exec::kill(p, exec::signal::SIGUSR1): void;
+ };
+ os::exit(1);
+};
+
@fini fn exit() void = {
os::rmdirall(tmpdir): void;
};
@@ -251,13 +279,14 @@ fn try_do(
const timestamp = fmt::asprintf("{},{}", timestamp.sec, timestamp.nsec);
defer free(timestamp);
exec::setenv(&cmd, "HAREDO_PARENT", timestamp)?;
+ exec::setenv(&cmd, "HAREDO_PID", strconv::itos(exec::self()))?;
// pass runtime environment variables down to child process
if (ctx.verbose) exec::setenv(&cmd, "HAREDO_VERBOSE", "1")?;
if (ctx.quiet) exec::setenv(&cmd, "HAREDO_QUIET", "1")?;
if (ctx.colorless) exec::setenv(&cmd, "HAREDO_COLORLESS", "1")?;
- if (ctx.toplevel) {
+ if (ctx.parent is void) {
// set default program variables in toplevel process
for (let i = 0z; i < len(envprogs); i += 1) {
const (var, value) = envprogs[i];
diff --git a/test.do b/test.do
index 0a511e0..46bbb9b 100644
--- a/test.do
+++ b/test.do
@@ -6,5 +6,5 @@ touch test/rv64/all.h
haredo test/all
haredo test/clean-gen
-haredo test/should-fail || true
-haredo test/unwriteable || true
+env -u HAREDO_PID haredo test/should-fail || true
+env -u HAREDO_PID haredo test/unwriteable || true
--
2.39.1
Thanks!
~Autumn