From a086aa995cfd3ee0d1555b85b86ca2abca962791 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Tue, 22 Sep 2015 21:43:22 +0200 Subject: [PATCH] Added parallelisation code & cleaned up runner code --- src/compat/process.c | 30 +++- src/compat/process.h | 3 + src/core/coroutine.h | 181 ++++++++++++++++++++++++ src/core/runner.c | 319 ++++++++++++++++++++++++------------------- src/core/runner.h | 2 - src/core/worker.c | 42 ++++-- src/core/worker.h | 40 ++++-- src/io/event.c | 24 ++-- src/io/event.h | 5 + 9 files changed, 465 insertions(+), 181 deletions(-) create mode 100644 src/core/coroutine.h diff --git a/src/compat/process.c b/src/compat/process.c index 72320d2..b107686 100644 --- a/src/compat/process.c +++ b/src/compat/process.c @@ -126,9 +126,12 @@ static void handle_sigchld(UNUSED int sig) { (s_proc_handle) { pid }, get_status(status) }; - char buf[sizeof (int) + sizeof (struct worker_status)]; + unsigned long long pid_ull = (unsigned long long) pid; + + char buf[sizeof (int) + sizeof (pid_ull) + sizeof (struct worker_status)]; memcpy(buf, &kind, sizeof (kind)); - memcpy(buf + sizeof (kind), &ws, sizeof (ws)); + memcpy(buf + sizeof (kind), &pid_ull, sizeof (pid_ull)); + memcpy(buf + sizeof (kind) + sizeof (pid_ull), &ws, sizeof (ws)); if (write(fd, &buf, sizeof (buf)) < (ssize_t) sizeof (buf)) abort(); @@ -155,9 +158,12 @@ static void CALLBACK handle_child_terminated(PVOID lpParameter, (s_proc_handle) { wctx->proc_handle }, get_status(status) }; - char buf[sizeof (int) + sizeof (struct worker_status)]; + unsigned long long pid_ull = (unsigned long long) GetProcessId(wctx->proc_handle); + + char buf[sizeof (int) + sizeof (pid_ull) + sizeof (struct worker_status)]; memcpy(buf, &kind, sizeof (kind)); - memcpy(buf + sizeof (kind), &ws, sizeof (ws)); + memcpy(buf + sizeof (kind), &pid_ull, sizeof (pid_ull)); + memcpy(buf + sizeof (kind) + sizeof (pid_ull), &ws, sizeof (ws)); DWORD written; WriteFile(g_worker_pipe->fhs[1], buf, sizeof (buf), &written, NULL); @@ -406,3 +412,19 @@ bool is_current_process(s_proc_handle *proc) { return proc->pid == getpid(); #endif } + +unsigned long long get_process_id(void) { +#ifdef VANILLA_WIN32 + return (unsigned long long) GetCurrentProcessId(); +#else + return (unsigned long long) getpid(); +#endif +} + +unsigned long long get_process_id_of(s_proc_handle *proc) { +#ifdef VANILLA_WIN32 + return (unsigned long long) GetProcessId(proc->handle); +#else + return (unsigned long long) proc->pid; +#endif +} diff --git a/src/compat/process.h b/src/compat/process.h index 0d70770..103d835 100644 --- a/src/compat/process.h +++ b/src/compat/process.h @@ -55,4 +55,7 @@ void wait_process(s_proc_handle *handle, int *status); s_proc_handle *get_current_process(); bool is_current_process(s_proc_handle *proc); +unsigned long long get_process_id(void); +unsigned long long get_process_id_of(s_proc_handle *proc); + #endif /* !COMPAT_PROCESS_H_ */ diff --git a/src/core/coroutine.h b/src/core/coroutine.h new file mode 100644 index 0000000..a8fad9b --- /dev/null +++ b/src/core/coroutine.h @@ -0,0 +1,181 @@ +/* coroutine.h + * + * Coroutine mechanics, implemented on top of standard ANSI C. See + * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for + * a full discussion of the theory behind this. + * + * To use these macros to define a coroutine, you need to write a + * function that looks something like this. + * + * [Simple version using static variables (scr macros)] + * int ascending (void) { + * static int i; + * + * scrBegin; + * for (i=0; i<10; i++) { + * scrReturn(i); + * } + * scrFinish(-1); + * } + * + * [Re-entrant version using an explicit context structure (ccr macros)] + * int ascending (ccrContParam) { + * ccrBeginContext; + * int i; + * ccrEndContext(foo); + * + * ccrBegin(foo); + * for (foo->i=0; foo->i<10; foo->i++) { + * ccrReturn(foo->i); + * } + * ccrFinish(-1); + * } + * + * In the static version, you need only surround the function body + * with `scrBegin' and `scrFinish', and then you can do `scrReturn' + * within the function and on the next call control will resume + * just after the scrReturn statement. Any local variables you need + * to be persistent across an `scrReturn' must be declared static. + * + * In the re-entrant version, you need to declare your persistent + * variables between `ccrBeginContext' and `ccrEndContext'. These + * will be members of a structure whose name you specify in the + * parameter to `ccrEndContext'. + * + * The re-entrant macros will malloc() the state structure on first + * call, and free() it when `ccrFinish' is reached. If you want to + * abort in the middle, you can use `ccrStop' to free the state + * structure immediately (equivalent to an explicit return() in a + * caller-type routine). + * + * A coroutine returning void type may call `ccrReturnV', + * `ccrFinishV' and `ccrStopV', or `scrReturnV', to avoid having to + * specify an empty parameter to the ordinary return macros. + * + * Ground rules: + * - never put `ccrReturn' or `scrReturn' within an explicit `switch'. + * - never put two `ccrReturn' or `scrReturn' statements on the same + * source line. + * + * The caller of a static coroutine calls it just as if it were an + * ordinary function: + * + * void main(void) { + * int i; + * do { + * i = ascending(); + * printf("got number %d\n", i); + * } while (i != -1); + * } + * + * The caller of a re-entrant coroutine must provide a context + * variable: + * + * void main(void) { + * ccrContext z = 0; + * do { + * printf("got number %d\n", ascending (&z)); + * } while (z); + * } + * + * Note that the context variable is set back to zero when the + * coroutine terminates (by crStop, or by control reaching + * crFinish). This can make the re-entrant coroutines more useful + * than the static ones, because you can tell when they have + * finished. + * + * If you need to dispose of a crContext when it is non-zero (that + * is, if you want to stop calling a coroutine without suffering a + * memory leak), the caller should call `ccrAbort(ctx)' where `ctx' + * is the context variable. + * + * This mechanism could have been better implemented using GNU C + * and its ability to store pointers to labels, but sadly this is + * not part of the ANSI C standard and so the mechanism is done by + * case statements instead. That's why you can't put a crReturn() + * inside a switch() statement. + */ + +/* + * coroutine.h is copyright 1995,2000 Simon Tatham. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef COROUTINE_H +#define COROUTINE_H + +#include + +/* + * `scr' macros for static coroutines. + */ + +#define scrBegin static int scrLine = 0; switch(scrLine) { case 0:; +#define scrFinish(z) } return (z) +#define scrFinishV } return + +#define scrReturn(z) \ + do {\ + scrLine=__LINE__;\ + return (z); case __LINE__:;\ + } while (0) +#define scrReturnV \ + do {\ + scrLine=__LINE__;\ + return; case __LINE__:;\ + } while (0) + +/* + * `ccr' macros for re-entrant coroutines. + */ + +#define ccrContParam void **ccrParam + +#define ccrBeginContext struct ccrContextTag { int ccrLine +#define ccrEndContext(x) } *x = (struct ccrContextTag *)*ccrParam + +#define ccrBegin(x) if(!x) {x= *ccrParam=malloc(sizeof(*x)); x->ccrLine=0;}\ + if (x) switch(x->ccrLine) { case 0:; +#define ccrFinish(z) } free(*ccrParam); *ccrParam=0; return (z) +#define ccrFinishV } free(*ccrParam); *ccrParam=0; return + +#define ccrReturn(z) \ + do {\ + ((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\ + return (z); case __LINE__:;\ + } while (0) +#define ccrReturnV \ + do {\ + ((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\ + return; case __LINE__:;\ + } while (0) + +#define ccrStop(z) do{ free(*ccrParam); *ccrParam=0; return (z); }while(0) +#define ccrStopV do{ free(*ccrParam); *ccrParam=0; return; }while(0) + +#define ccrContext void * +#define ccrAbort(ctx) do { free (ctx); ctx = 0; } while (0) + +#endif /* COROUTINE_H */ diff --git a/src/core/runner.c b/src/core/runner.c index a998843..67b5fad 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -39,6 +39,7 @@ #include "abort.h" #include "config.h" #include "common.h" +#include "coroutine.h" #ifdef HAVE_PCRE #include "string/extmatch.h" @@ -130,45 +131,6 @@ struct criterion_test_set *criterion_init(void) { return set; } -typedef void (*f_test_run)(struct criterion_global_stats *, - struct criterion_suite_stats *, - struct criterion_test *, - struct criterion_suite *); - -static void map_tests(struct criterion_test_set *set, - struct criterion_global_stats *stats, - f_test_run fun) { - - FOREACH_SET(struct criterion_suite_set *s, set->suites) { - if (!s->tests) - continue; - - report(PRE_SUITE, s); - log(pre_suite, s); - - struct criterion_suite_stats *suite_stats = suite_stats_init(&s->suite); - - struct event ev = { .kind = PRE_SUITE }; - stat_push_event(stats, suite_stats, NULL, &ev); - - FOREACH_SET(struct criterion_test *t, s->tests) { - fun(stats, suite_stats, t, &s->suite); - if (criterion_options.fail_fast && stats->tests_failed > 0) - break; - if (!is_runner()) { - sfree(suite_stats); - return; - } - } - - report(POST_SUITE, suite_stats); - log(post_suite, suite_stats); - - sfree(suite_stats); - } - -} - static void run_test_child(struct criterion_test *test, struct criterion_suite *suite) { @@ -224,17 +186,6 @@ static INLINE bool is_disabled(struct criterion_test *t, s_pipe_handle *g_worker_pipe; -struct execution_context { - bool test_started; - bool normal_finish; - bool cleaned_up; - struct criterion_global_stats *stats; - struct criterion_test *test; - struct criterion_test_stats *test_stats; - struct criterion_suite *suite; - struct criterion_suite_stats *suite_stats; -}; - static void handle_worker_terminated(struct event *ev, struct execution_context *ctx) { @@ -301,120 +252,74 @@ static void handle_worker_terminated(struct event *ev, } } -static void run_test(struct criterion_global_stats *stats, +static void handle_event(struct event *ev) { + struct execution_context *ctx = &ev->worker->ctx; + if (ev->kind < WORKER_TERMINATED) + stat_push_event(ctx->stats, ctx->suite_stats, ctx->test_stats, ev); + switch (ev->kind) { + case PRE_INIT: + report(PRE_INIT, ctx->test); + log(pre_init, ctx->test); + break; + case PRE_TEST: + report(PRE_TEST, ctx->test); + log(pre_test, ctx->test); + ctx->test_started = true; + break; + case THEORY_FAIL: { + struct criterion_theory_stats ths = { + .formatted_args = (char*) ev->data, + .stats = ctx->test_stats, + }; + report(THEORY_FAIL, &ths); + log(theory_fail, &ths); + } break; + case ASSERT: + report(ASSERT, ev->data); + log(assert, ev->data); + break; + case POST_TEST: + report(POST_TEST, ctx->test_stats); + log(post_test, ctx->test_stats); + ctx->normal_finish = true; + break; + case POST_FINI: + report(POST_FINI, ctx->test_stats); + log(post_fini, ctx->test_stats); + ctx->cleaned_up = true; + break; + case WORKER_TERMINATED: + handle_worker_terminated(ev, ctx); + break; + } +} + +static struct process *run_test(struct criterion_global_stats *stats, struct criterion_suite_stats *suite_stats, struct criterion_test *test, struct criterion_suite *suite, struct test_single_param *param) { struct criterion_test_stats *test_stats = test_stats_init(test); - struct process *proc = NULL; if (is_disabled(test, suite)) { stat_push_event(stats, suite_stats, test_stats, &(struct event) { .kind = PRE_INIT }); - goto cleanup; + return NULL; } - proc = spawn_test_worker(test, suite, run_test_child, g_worker_pipe, param); - if (proc == NULL && !is_runner()) - goto cleanup; - struct execution_context ctx = { .stats = stats, .test = test, .test_stats = test_stats, .suite = suite, .suite_stats = suite_stats, + .param = param, }; - struct event *ev; - while ((ev = worker_read_event(proc)) != NULL) { - if (ev->kind < WORKER_TERMINATED) - stat_push_event(stats, suite_stats, test_stats, ev); - switch (ev->kind) { - case PRE_INIT: - report(PRE_INIT, test); - log(pre_init, test); - break; - case PRE_TEST: - report(PRE_TEST, test); - log(pre_test, test); - ctx.test_started = true; - break; - case THEORY_FAIL: { - struct criterion_theory_stats ths = { - .formatted_args = (char*) ev->data, - .stats = test_stats, - }; - report(THEORY_FAIL, &ths); - log(theory_fail, &ths); - } break; - case ASSERT: - report(ASSERT, ev->data); - log(assert, ev->data); - break; - case POST_TEST: - report(POST_TEST, test_stats); - log(post_test, test_stats); - ctx.normal_finish = true; - break; - case POST_FINI: - report(POST_FINI, test_stats); - log(post_fini, test_stats); - ctx.cleaned_up = true; - break; - case WORKER_TERMINATED: - handle_worker_terminated(ev, &ctx); - sfree(ev); - goto cleanup; - } - sfree(ev); - } + return spawn_test_worker(&ctx, run_test_child, g_worker_pipe); -cleanup: - sfree(test_stats); - sfree(proc); -} - -static void run_test_param(struct criterion_global_stats *stats, - struct criterion_suite_stats *suite_stats, - struct criterion_test *test, - struct criterion_suite *suite) { - - if (!test->data->param_) - return; - - struct criterion_test_params params = test->data->param_(); - for (size_t i = 0; i < params.length; ++i) { - struct test_single_param param = { params.size, (char *) params.params + i * params.size }; - - run_test(stats, suite_stats, test, suite, ¶m); - if (criterion_options.fail_fast && stats->tests_failed > 0) - break; - if (!is_runner()) - break; - } - - if (params.cleanup) - params.cleanup(¶ms); -} - -static void run_test_switch(struct criterion_global_stats *stats, - struct criterion_suite_stats *suite_stats, - struct criterion_test *test, - struct criterion_suite *suite) { - - switch (test->data->kind_) { - case CR_TEST_NORMAL: - run_test(stats, suite_stats, test, suite, NULL); - break; - case CR_TEST_PARAMETERIZED: - run_test_param(stats, suite_stats, test, suite); - break; - default: break; - } } #ifdef HAVE_PCRE @@ -450,6 +355,134 @@ void criterion_finalize(struct criterion_test_set *set) { sfree(set); } +static struct process *run_next_test(struct criterion_test_set *p_set, + struct criterion_global_stats *p_stats, + ccrContParam) { + ccrBeginContext; + struct criterion_suite_set *suite_set; + struct criterion_test *test; + struct criterion_suite_stats *suite_stats; + struct criterion_test_set *set; + struct criterion_global_stats *stats; + struct criterion_test_params params; + + struct criterion_ordered_set_node *ns; + struct criterion_ordered_set_node *nt; + size_t i; + ccrEndContext(ctx); + + ccrBegin(ctx); + + ctx->set = p_set; + ctx->stats = p_stats; + ccrReturn(NULL); + + for (ctx->ns = ctx->set->suites->first; ctx->ns; ctx->ns = ctx->ns->next) { + ctx->suite_set = (void*) ctx->ns->data; + + if (!ctx->suite_set->tests) + continue; + + report(PRE_SUITE, ctx->suite_set); + log(pre_suite, ctx->suite_set); + + ctx->suite_stats = suite_stats_init(&ctx->suite_set->suite); + + struct event ev = { .kind = PRE_SUITE }; + stat_push_event(ctx->stats, ctx->suite_stats, NULL, &ev); + + for (ctx->nt = ctx->suite_set->tests->first; ctx->nt; ctx->nt = ctx->nt->next) { + ctx->test = (void*) ctx->nt->data; + + if (ctx->test->data->kind_ == CR_TEST_PARAMETERIZED + && ctx->test->data->param_) { + + ctx->params = ctx->test->data->param_(); + for (ctx->i = 0; ctx->i < ctx->params.length; ++ctx->i) { + struct test_single_param param = { + ctx->params.size, + (char *) ctx->params.params + ctx->i * ctx->params.size + }; + + struct process *worker = run_test(ctx->stats, + ctx->suite_stats, + ctx->test, + &ctx->suite_set->suite, + ¶m); + + if (!is_runner()) { + sfree(ctx->suite_stats); + ccrReturn(NULL); + } + + ccrReturn(worker); + } + + if (ctx->params.cleanup) + ctx->params.cleanup(&ctx->params); + } else { + struct process *worker = run_test(ctx->stats, + ctx->suite_stats, + ctx->test, + &ctx->suite_set->suite, + NULL); + + if (!is_runner()) { + sfree(ctx->suite_stats); + ccrReturn(NULL); + } + + ccrReturn(worker); + } + } + + report(POST_SUITE, ctx->suite_stats); + log(post_suite, ctx->suite_stats); + + sfree(ctx->suite_stats); + } + + ccrFinish(NULL); +} + +static void run_tests_async(struct criterion_test_set *set, + struct criterion_global_stats *stats) { + + ccrContext ctx = 0; + + size_t nb_workers = 1; + struct worker_set workers = { + .max_workers = nb_workers, + .workers = malloc(sizeof (struct process*) * nb_workers), + }; + + size_t active_workers = 0; + + // initialization of coroutine + run_next_test(set, stats, &ctx); + + for (size_t i = 0; i < nb_workers; ++i) { + workers.workers[i] = run_next_test(NULL, NULL, &ctx); + ++active_workers; + } + + FILE *event_pipe = pipe_in(g_worker_pipe, PIPE_DUP); + + struct event *ev; + while ((ev = worker_read_event(&workers, event_pipe)) != NULL) { + handle_event(ev); + if (ev->kind == WORKER_TERMINATED) { + workers.workers[ev->worker_index] = run_next_test(NULL, NULL, &ctx); + if (workers.workers[ev->worker_index] == NULL) + --active_workers; + } + sfree(ev); + if (!active_workers) + break; + } + +} + static int criterion_run_all_tests_impl(struct criterion_test_set *set) { report(PRE_ALL, set); log(pre_all, set); @@ -461,7 +494,7 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) { abort(); struct criterion_global_stats *stats = stats_init(); - map_tests(set, stats, run_test_switch); + run_tests_async(set, stats); int result = is_runner() ? stats->tests_failed == 0 : -1; diff --git a/src/core/runner.h b/src/core/runner.h index 0b6ad05..9ddb57f 100644 --- a/src/core/runner.h +++ b/src/core/runner.h @@ -42,6 +42,4 @@ struct criterion_test_set *criterion_init(void); Suite < (struct criterion_suite**) GET_SECTION_END(cr_sts); \ ++Suite) -extern s_pipe_handle *g_worker_pipe; - #endif /* !CRITERION_RUNNER_H_ */ diff --git a/src/core/worker.c b/src/core/worker.c index 85a945a..1dd6861 100644 --- a/src/core/worker.c +++ b/src/core/worker.c @@ -32,11 +32,6 @@ #include "compat/posix.h" #include "worker.h" -struct process { - s_proc_handle *proc; - FILE *in; -}; - static s_proc_handle *g_current_proc; void set_runner_process(void) { @@ -57,8 +52,23 @@ static void close_process(void *ptr, UNUSED void *meta) { sfree(proc->proc); } -struct event *worker_read_event(struct process *proc) { - return read_event(proc->in); +struct event *worker_read_event(struct worker_set *workers, FILE *pipe) { + struct event *ev = read_event(pipe); + if (ev) { + ev->worker_index = -1; + for (size_t i = 0; i < workers->max_workers; ++i) { + if (!workers->workers[i]) + continue; + + if (get_process_id_of(workers->workers[i]->proc) == ev->pid) { + ev->worker = workers->workers[i]; + ev->worker_index = i; + return ev; + } + } + abort(); + } + return NULL; } void run_worker(struct worker_context *ctx) { @@ -74,17 +84,15 @@ void run_worker(struct worker_context *ctx) { _Exit(0); } -struct process *spawn_test_worker(struct criterion_test *test, - struct criterion_suite *suite, +struct process *spawn_test_worker(struct execution_context *ctx, f_worker_func func, - s_pipe_handle *pipe, - struct test_single_param *param) { + s_pipe_handle *pipe) { g_worker_context = (struct worker_context) { - .test = test, - .suite = suite, + .test = ctx->test, + .suite = ctx->suite, .func = func, .pipe = pipe, - .param = param, + .param = ctx->param, }; struct process *ptr = NULL; @@ -102,7 +110,11 @@ struct process *spawn_test_worker(struct criterion_test *test, .kind = UNIQUE, .dtor = close_process); - *ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, PIPE_DUP) }; + *ptr = (struct process) { + .proc = proc, + .in = pipe_in(pipe, PIPE_DUP), + .ctx = *ctx, + }; return ptr; } diff --git a/src/core/worker.h b/src/core/worker.h index ab8bff9..e7b2c7a 100644 --- a/src/core/worker.h +++ b/src/core/worker.h @@ -29,7 +29,29 @@ # include "compat/process.h" # include "compat/pipe.h" -struct process; +struct test_single_param { + size_t size; + void *ptr; +}; + +struct execution_context { + bool test_started; + bool normal_finish; + bool cleaned_up; + struct criterion_global_stats *stats; + struct criterion_test *test; + struct criterion_test_stats *test_stats; + struct criterion_suite *suite; + struct criterion_suite_stats *suite_stats; + struct test_single_param *param; +}; + +struct process { + int active; + s_proc_handle *proc; + FILE *in; + struct execution_context ctx; +}; enum status_kind { EXIT_STATUS, @@ -47,22 +69,22 @@ struct worker_status { struct process_status status; }; -struct test_single_param { - size_t size; - void *ptr; +struct worker_set { + struct process **workers; + size_t max_workers; }; +extern s_pipe_handle *g_worker_pipe; + void run_worker(struct worker_context *ctx); void set_runner_process(void); void unset_runner_process(void); bool is_runner(void); struct process_status wait_proc(struct process *proc); struct process_status get_status(int status); -struct process *spawn_test_worker(struct criterion_test *test, - struct criterion_suite *suite, +struct process *spawn_test_worker(struct execution_context *ctx, f_worker_func func, - s_pipe_handle *pipe, - struct test_single_param *param); -struct event *worker_read_event(struct process *proc); + s_pipe_handle *pipe); +struct event *worker_read_event(struct worker_set *workers, FILE *pipe); #endif /* !PROCESS_H_ */ diff --git a/src/io/event.c b/src/io/event.c index 1ba6a26..4760020 100644 --- a/src/io/event.c +++ b/src/io/event.c @@ -49,6 +49,10 @@ struct event *read_event(FILE *f) { if (fread(&kind, sizeof (unsigned), 1, f) == 0) return NULL; + unsigned long long pid; + if (fread(&pid, sizeof (unsigned long long), 1, f) == 0) + return NULL; + switch (kind) { case ASSERT: { const size_t assert_size = sizeof (struct criterion_assert_stats); @@ -73,7 +77,7 @@ struct event *read_event(FILE *f) { .size = sizeof (struct event), .dtor = destroy_assert_event ); - *ev = (struct event) { .kind = kind, .data = buf }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = buf }; return ev; fail_assert: @@ -96,7 +100,7 @@ fail_assert: .size = sizeof (struct event), .dtor = destroy_event ); - *ev = (struct event) { .kind = kind, .data = buf }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = buf }; return ev; } case POST_TEST: { @@ -110,7 +114,7 @@ fail_assert: .size = sizeof (struct event), .dtor = destroy_event ); - *ev = (struct event) { .kind = kind, .data = elapsed_time }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = elapsed_time }; return ev; } case WORKER_TERMINATED: { @@ -124,22 +128,26 @@ fail_assert: .size = sizeof (struct event), .dtor = destroy_event ); - *ev = (struct event) { .kind = kind, .data = status }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = status }; return ev; } default: { struct event *ev = smalloc(sizeof (struct event)); - *ev = (struct event) { .kind = kind, .data = NULL }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = NULL }; return ev; } } } void send_event(int kind, void *data, size_t size) { - unsigned char *buf = malloc(sizeof (int) + size); + unsigned long long pid = get_process_id(); + + unsigned char *buf = malloc(sizeof (int) + sizeof (pid) + size); memcpy(buf, &kind, sizeof (int)); - memcpy(buf + sizeof (int), data, size); - if (fwrite(buf, sizeof (int) + size, 1, g_event_pipe) == 0) + memcpy(buf + sizeof (int), &pid, sizeof (pid)); + memcpy(buf + sizeof (int) + sizeof (pid), data, size); + if (fwrite(buf, sizeof (int) + sizeof (pid) + size, 1, g_event_pipe) == 0) abort(); + free(buf); } diff --git a/src/io/event.h b/src/io/event.h index 64c30ac..faa54d3 100644 --- a/src/io/event.h +++ b/src/io/event.h @@ -25,13 +25,18 @@ # define EVENT_H_ # include "criterion/event.h" +# include "core/worker.h" # include extern FILE *g_event_pipe; struct event { + unsigned long long pid; int kind; void *data; + + struct process *worker; + size_t worker_index; }; enum other_event_kinds {