Added parallelisation code & cleaned up runner code

This commit is contained in:
Snaipe 2015-09-22 21:43:22 +02:00
parent 3af74ac9dd
commit a086aa995c
9 changed files with 465 additions and 181 deletions

View file

@ -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
}

View file

@ -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_ */

181
src/core/coroutine.h Normal file
View file

@ -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 <stdlib.h>
/*
* `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 */

View file

@ -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, &param);
if (criterion_options.fail_fast && stats->tests_failed > 0)
break;
if (!is_runner())
break;
}
if (params.cleanup)
params.cleanup(&params);
}
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,
&param);
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;

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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);
}

View file

@ -25,13 +25,18 @@
# define EVENT_H_
# include "criterion/event.h"
# include "core/worker.h"
# include <stdio.h>
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 {