From df1f23ef4cf121e1bdeee2a66428c28885e3835d Mon Sep 17 00:00:00 2001 From: Snaipe Date: Tue, 10 Feb 2015 18:20:06 +0100 Subject: [PATCH] Refactored process management into its own file --- Makefile.am | 2 + src/process.c | 71 +++++++++++++++++++++++++++++++++++ src/process.h | 23 ++++++++++++ src/runner.c | 102 ++++++++++++++++++-------------------------------- 4 files changed, 132 insertions(+), 66 deletions(-) create mode 100644 src/process.c create mode 100644 src/process.h diff --git a/Makefile.am b/Makefile.am index 684c818..fc4f8b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,5 +37,7 @@ libcriterion_la_SOURCES = \ src/report.h \ src/runner.c \ src/runner.h \ + src/process.c \ + src/process.h \ src/stats.c \ src/stats.h diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..3cbab45 --- /dev/null +++ b/src/process.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include + +#include "criterion/criterion.h" +#include "process.h" +#include "event.h" + +struct process { + pid_t pid; + int in; +}; + +static pid_t g_runner_pid; + +void set_runner_pid(void) { + g_runner_pid = getpid(); +} + +bool is_runner(void) { + return g_runner_pid == getpid(); +} + +static void close_process(void *ptr, UNUSED void *meta) { + close(((struct process *) ptr)->in); +} + +struct event *worker_read_event(struct process *proc) { + return read_event(proc->in); +} + +struct process *spawn_test_worker(struct criterion_test *test, void (*func)(struct criterion_test *)) { + int fds[2]; + if (pipe(fds) == -1) + abort(); + + pid_t pid = fork(); + if (pid == -1) { + return NULL; + } else if (!pid) { + close(STDIN_FILENO); + close(fds[0]); + EVENT_PIPE = fds[1]; + + func(test); + close(fds[1]); + if (!strcmp("1", getenv("CRITERION_NO_EARLY_EXIT") ?: "0")) + return NULL; + else + _exit(0); + } + + close(fds[1]); + return unique_ptr(struct process, ({ .pid = pid, .in = fds[0] }), close_process); +} + +struct process_status wait_proc(struct process *proc) { + int status; + waitpid(proc->pid, &status, 0); + + if (WIFEXITED(status)) + return (struct process_status) { .kind = EXIT_STATUS, .status = WEXITSTATUS(status) }; + + if (WIFSIGNALED(status)) + return (struct process_status) { .kind = SIGNAL, .status = WTERMSIG(status) }; + + return (struct process_status) { .kind = STOPPED }; +} diff --git a/src/process.h b/src/process.h new file mode 100644 index 0000000..9eb3358 --- /dev/null +++ b/src/process.h @@ -0,0 +1,23 @@ +#ifndef PROCESS_H_ +# define PROCESS_H_ + +struct process; + +enum status_kind { + EXIT_STATUS, + STOPPED, + SIGNAL, +}; + +struct process_status { + enum status_kind kind; + int status; +}; + +void set_runner_pid(void); +bool is_runner(void); +struct process_status wait_proc(struct process *proc); +struct process *spawn_test_worker(struct criterion_test *test, void (*func)(struct criterion_test *)); +struct event *worker_read_event(struct process *proc); + +#endif /* !PROCESS_H_ */ diff --git a/src/runner.c b/src/runner.c index 5d7afc3..ddc30fe 100644 --- a/src/runner.c +++ b/src/runner.c @@ -31,35 +31,26 @@ #include "runner.h" #include "report.h" #include "event.h" +#include "process.h" static struct criterion_test * const g_section_start = &__start_criterion_tests; static struct criterion_test * const g_section_end = &__stop_criterion_tests; -static pid_t g_runner_pid; - struct test_set { struct criterion_test **tests; size_t nb_tests; }; -static int compare_test_by_name(const struct criterion_test *first, - const struct criterion_test *second) { - // likely to happen - if (first->name == second->name) - return 0; - return strcmp(first->name, second->name); -} - static int compare_test(const void *a, const void *b) { struct criterion_test *first = *(struct criterion_test **) a; struct criterion_test *second = *(struct criterion_test **) b; // likely to happen if (first->category == second->category) { - return compare_test_by_name(first, second); + return strcmp(first->name, second->name); } else { return strcmp(first->category, second->category) - ?: compare_test_by_name(first, second); + ?: strcmp(first->name, second->name); } } @@ -91,7 +82,7 @@ static void map_tests(struct test_set *set, struct criterion_global_stats *stats size_t i = 0; for (struct criterion_test **t = set->tests; i < set->nb_tests; ++i, ++t) { fun(stats, *t); - if (g_runner_pid != getpid()) + if (!is_runner()) return; } } @@ -109,63 +100,41 @@ static void run_test_child(struct criterion_test *test) { send_event(POST_FINI, NULL, 0); } -struct pipefds { - int in, out; -} __attribute__ ((packed)); - -static void setup_child(struct pipefds *fds) { - close(STDIN_FILENO); - close(fds->in); - EVENT_PIPE = fds->out; -} - static void run_test(struct criterion_global_stats *stats, struct criterion_test *test) { smart struct criterion_test_stats *test_stats = test_stats_init(test); - struct pipefds fds; - if (pipe((int*) &fds) == -1) - abort(); + smart struct process *proc = spawn_test_worker(test, run_test_child); + if (proc == NULL && !is_runner()) + return; - pid_t pid; - if (!(pid = fork())) { - setup_child(&fds); - - run_test_child(test); - if (!strcmp("1", getenv("CRITERION_NO_EARLY_EXIT") ?: "0")) - return; - else - _exit(0); - } else { - close(fds.out); - struct event *ev; - while ((ev = read_event(fds.in)) != NULL) { - stat_push_event(stats, test_stats, ev); - switch (ev->kind) { - case PRE_INIT: report(PRE_INIT, test); break; - case PRE_TEST: report(PRE_TEST, test); break; - case ASSERT: report(ASSERT, ev->data); break; - case POST_TEST: report(POST_TEST, test_stats); break; - case POST_FINI: report(POST_FINI, test_stats); break; - } - sfree(ev); + struct event *ev; + while ((ev = worker_read_event(proc)) != NULL) { + stat_push_event(stats, test_stats, ev); + switch (ev->kind) { + case PRE_INIT: report(PRE_INIT, test); break; + case PRE_TEST: report(PRE_TEST, test); break; + case ASSERT: report(ASSERT, ev->data); break; + case POST_TEST: report(POST_TEST, test_stats); break; + case POST_FINI: report(POST_FINI, test_stats); break; } - int status; - waitpid(pid, &status, 0); - if (WIFSIGNALED(status)) { - test_stats->signal = WTERMSIG(status); - if (test->data->signal == 0) { - struct event ev = { .kind = TEST_CRASH }; - stat_push_event(stats, test_stats, &ev); - report(TEST_CRASH, test_stats); - } else { - struct event ev = { .kind = POST_TEST }; - stat_push_event(stats, test_stats, &ev); - report(POST_TEST, test_stats); + sfree(ev); + } - ev.kind = POST_FINI; - stat_push_event(stats, test_stats, &ev); - report(POST_FINI, test_stats); - } + struct process_status status = wait_proc(proc); + if (status.kind == SIGNAL) { + test_stats->signal = status.status; + if (test->data->signal == 0) { + struct event ev = { .kind = TEST_CRASH }; + stat_push_event(stats, test_stats, &ev); + report(TEST_CRASH, test_stats); + } else { + struct event ev = { .kind = POST_TEST }; + stat_push_event(stats, test_stats, &ev); + report(POST_TEST, test_stats); + + ev.kind = POST_FINI; + stat_push_event(stats, test_stats, &ev); + report(POST_FINI, test_stats); } } } @@ -173,14 +142,15 @@ static void run_test(struct criterion_global_stats *stats, struct criterion_test // TODO: disable & change tests at runtime static int criterion_run_all_tests_impl(void) { report(PRE_EVERYTHING, NULL); - g_runner_pid = getpid(); + set_runner_pid(); + smart struct test_set *set = read_all_tests(); smart struct criterion_global_stats *stats = stats_init(); if (!set) abort(); map_tests(set, stats, run_test); - if (g_runner_pid != getpid()) + if (!is_runner()) return -1; report(POST_EVERYTHING, stats);