Merge branch 'features/async-io' into bleeding
This commit is contained in:
commit
864b600038
11 changed files with 314 additions and 145 deletions
|
@ -24,9 +24,7 @@
|
|||
#ifndef INTERNAL_H_
|
||||
# define INTERNAL_H_
|
||||
|
||||
# include "posix.h"
|
||||
|
||||
# ifdef VANILLA_WIN32
|
||||
# if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
# define VC_EXTRALEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# undef _WIN32_WINNT
|
||||
|
@ -44,4 +42,6 @@
|
|||
# include <sys/fcntl.h>
|
||||
# endif
|
||||
|
||||
# include "posix.h"
|
||||
|
||||
#endif /* !INTERNAL_H_ */
|
||||
|
|
|
@ -27,18 +27,23 @@
|
|||
#include "criterion/assert.h"
|
||||
#include "pipe-internal.h"
|
||||
|
||||
FILE *pipe_in(s_pipe_handle *p, int do_close) {
|
||||
FILE *pipe_in(s_pipe_handle *p, enum pipe_opt opts) {
|
||||
#ifdef VANILLA_WIN32
|
||||
if (do_close)
|
||||
if (opts & PIPE_CLOSE)
|
||||
CloseHandle(p->fhs[1]);
|
||||
int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
if (opts & PIPE_DUP)
|
||||
fd = _dup(fd);
|
||||
FILE *in = _fdopen(fd, "r");
|
||||
#else
|
||||
if (do_close)
|
||||
if (opts & PIPE_CLOSE)
|
||||
close(p->fds[1]);
|
||||
FILE *in = fdopen(p->fds[0], "r");
|
||||
int fd = p->fds[0];
|
||||
if (opts & PIPE_DUP)
|
||||
fd = dup(fd);
|
||||
FILE *in = fdopen(fd, "r");
|
||||
#endif
|
||||
if (!in)
|
||||
return NULL;
|
||||
|
@ -47,18 +52,23 @@ FILE *pipe_in(s_pipe_handle *p, int do_close) {
|
|||
return in;
|
||||
}
|
||||
|
||||
FILE *pipe_out(s_pipe_handle *p, int do_close) {
|
||||
FILE *pipe_out(s_pipe_handle *p, enum pipe_opt opts) {
|
||||
#ifdef VANILLA_WIN32
|
||||
if (do_close)
|
||||
if (opts & PIPE_CLOSE)
|
||||
CloseHandle(p->fhs[0]);
|
||||
int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
if (opts & PIPE_DUP)
|
||||
fd = _dup(fd);
|
||||
FILE *out = _fdopen(fd, "w");
|
||||
#else
|
||||
if (do_close)
|
||||
if (opts & PIPE_CLOSE)
|
||||
close(p->fds[0]);
|
||||
FILE *out = fdopen(p->fds[1], "w");
|
||||
int fd = p->fds[1];
|
||||
if (opts & PIPE_DUP)
|
||||
fd = dup(fd);
|
||||
FILE *out = fdopen(fd, "w");
|
||||
#endif
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#ifndef PIPE_H_
|
||||
# define PIPE_H_
|
||||
|
||||
# include <stdio.h>
|
||||
# include "common.h"
|
||||
|
||||
struct pipe_handle;
|
||||
|
@ -40,9 +41,14 @@ enum criterion_std_fd {
|
|||
CR_STDERR = 2,
|
||||
};
|
||||
|
||||
enum pipe_opt {
|
||||
PIPE_DUP = 1 << 0,
|
||||
PIPE_CLOSE = 1 << 1,
|
||||
};
|
||||
|
||||
s_pipe_handle *stdpipe();
|
||||
FILE *pipe_in(s_pipe_handle *p, int do_close);
|
||||
FILE *pipe_out(s_pipe_handle *p, int do_close);
|
||||
FILE *pipe_in(s_pipe_handle *p, enum pipe_opt opts);
|
||||
FILE *pipe_out(s_pipe_handle *p, enum pipe_opt opts);
|
||||
|
||||
int stdpipe_options(s_pipe_handle *pipe, int id, int noblock);
|
||||
void pipe_std_redirect(s_pipe_handle *pipe, enum criterion_std_fd fd);
|
||||
|
|
|
@ -21,12 +21,18 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <csptr/smalloc.h>
|
||||
#include "core/worker.h"
|
||||
#include "core/runner.h"
|
||||
#include "io/event.h"
|
||||
#include "process.h"
|
||||
#include "internal.h"
|
||||
#include "pipe-internal.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
# define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \
|
||||
CreateProcessW(Filename, \
|
||||
|
@ -42,15 +48,46 @@
|
|||
|
||||
# define WRITE_PROCESS_(Proc, What, Size) \
|
||||
WriteProcessMemory(Proc, &What, &What, Size, NULL);
|
||||
|
||||
static int get_win_status(HANDLE handle) {
|
||||
DWORD exit_code;
|
||||
GetExitCodeProcess(handle, &exit_code);
|
||||
unsigned int sig = 0;
|
||||
switch (exit_code) {
|
||||
case STATUS_FLOAT_DENORMAL_OPERAND:
|
||||
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
||||
case STATUS_FLOAT_INEXACT_RESULT:
|
||||
case STATUS_FLOAT_INVALID_OPERATION:
|
||||
case STATUS_FLOAT_OVERFLOW:
|
||||
case STATUS_FLOAT_STACK_CHECK:
|
||||
case STATUS_FLOAT_UNDERFLOW:
|
||||
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
||||
case STATUS_INTEGER_OVERFLOW: sig = SIGFPE; break;
|
||||
|
||||
case STATUS_ILLEGAL_INSTRUCTION:
|
||||
case STATUS_PRIVILEGED_INSTRUCTION:
|
||||
case STATUS_NONCONTINUABLE_EXCEPTION: sig = SIGILL; break;
|
||||
|
||||
case CR_EXCEPTION_TIMEOUT: sig = SIGPROF; break;
|
||||
|
||||
case STATUS_ACCESS_VIOLATION:
|
||||
case STATUS_DATATYPE_MISALIGNMENT:
|
||||
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
||||
case STATUS_GUARD_PAGE_VIOLATION:
|
||||
case STATUS_IN_PAGE_ERROR:
|
||||
case STATUS_NO_MEMORY:
|
||||
case STATUS_INVALID_DISPOSITION:
|
||||
case STATUS_STACK_OVERFLOW: sig = SIGSEGV; break;
|
||||
|
||||
case STATUS_CONTROL_C_EXIT: sig = SIGINT; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return sig ? sig : exit_code << 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct proc_handle {
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE handle;
|
||||
#else
|
||||
pid_t pid;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct worker_context g_worker_context = {.test = NULL};
|
||||
|
||||
|
@ -71,6 +108,64 @@ static TCHAR g_mapping_name[] = TEXT("WinCriterionWorker");
|
|||
static struct full_context local_ctx;
|
||||
#endif
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
# ifndef __GNUC__
|
||||
# error Unsupported compiler. Use GCC or Clang under *nixes.
|
||||
# endif
|
||||
|
||||
static void handle_sigchld(int sig) {
|
||||
assert(sig == SIGCHLD);
|
||||
|
||||
int fd = g_worker_pipe->fds[1];
|
||||
pid_t pid;
|
||||
int status;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
int kind = WORKER_TERMINATED;
|
||||
struct worker_status ws = {
|
||||
(s_proc_handle) { pid }, get_status(status)
|
||||
};
|
||||
|
||||
char buf[sizeof (int) + sizeof (struct worker_status)];
|
||||
memcpy(buf, &kind, sizeof (kind));
|
||||
memcpy(buf + sizeof (kind), &ws, sizeof (ws));
|
||||
|
||||
write(fd, &buf, sizeof (buf));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
struct wait_context {
|
||||
HANDLE wait_handle;
|
||||
HANDLE proc_handle;
|
||||
};
|
||||
|
||||
static void CALLBACK handle_child_terminated(PVOID lpParameter,
|
||||
BOOLEAN TimerOrWaitFired) {
|
||||
|
||||
assert(!TimerOrWaitFired);
|
||||
|
||||
struct wait_context *wctx = lpParameter;
|
||||
|
||||
int status = get_win_status(wctx->proc_handle);
|
||||
int kind = WORKER_TERMINATED;
|
||||
struct worker_status ws = {
|
||||
(s_proc_handle) { wctx->proc_handle }, get_status(status)
|
||||
};
|
||||
|
||||
char buf[sizeof (int) + sizeof (struct worker_status)];
|
||||
memcpy(buf, &kind, sizeof (kind));
|
||||
memcpy(buf + sizeof (kind), &ws, sizeof (ws));
|
||||
|
||||
DWORD written;
|
||||
WriteFile(g_worker_pipe->fhs[1], buf, sizeof (buf), &written, NULL);
|
||||
|
||||
HANDLE whandle = wctx->wait_handle;
|
||||
free(lpParameter);
|
||||
UnregisterWaitEx(whandle, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
int resume_child(void) {
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE sharedMem = OpenFileMapping(
|
||||
|
@ -111,6 +206,16 @@ int resume_child(void) {
|
|||
run_worker(&g_worker_context);
|
||||
return 1;
|
||||
#else
|
||||
# if defined(__unix__) || defined(__APPLE__)
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = &handle_sigchld;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||||
if (sigaction(SIGCHLD, &sa, 0) == -1) {
|
||||
perror(0);
|
||||
exit(1);
|
||||
}
|
||||
# endif
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
@ -172,6 +277,19 @@ s_proc_handle *fork_process() {
|
|||
UnmapViewOfFile(ctx);
|
||||
CloseHandle(sharedMem);
|
||||
|
||||
struct wait_context *wctx = malloc(sizeof (struct wait_context));
|
||||
*wctx = (struct wait_context) {
|
||||
.proc_handle = info.hProcess,
|
||||
};
|
||||
|
||||
RegisterWaitForSingleObject(
|
||||
&wctx->wait_handle,
|
||||
info.hProcess,
|
||||
handle_child_terminated,
|
||||
wctx,
|
||||
INFINITE,
|
||||
WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE);
|
||||
|
||||
s_proc_handle *handle = smalloc(sizeof (s_proc_handle));
|
||||
*handle = (s_proc_handle) { info.hProcess };
|
||||
return handle;
|
||||
|
@ -191,43 +309,8 @@ s_proc_handle *fork_process() {
|
|||
void wait_process(s_proc_handle *handle, int *status) {
|
||||
#ifdef VANILLA_WIN32
|
||||
WaitForSingleObject(handle->handle, INFINITE);
|
||||
DWORD exit_code;
|
||||
GetExitCodeProcess(handle->handle, &exit_code);
|
||||
*status = get_win_status(handle->handle);
|
||||
CloseHandle(handle->handle);
|
||||
|
||||
unsigned int sig = 0;
|
||||
switch (exit_code) {
|
||||
case STATUS_FLOAT_DENORMAL_OPERAND:
|
||||
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
||||
case STATUS_FLOAT_INEXACT_RESULT:
|
||||
case STATUS_FLOAT_INVALID_OPERATION:
|
||||
case STATUS_FLOAT_OVERFLOW:
|
||||
case STATUS_FLOAT_STACK_CHECK:
|
||||
case STATUS_FLOAT_UNDERFLOW:
|
||||
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
||||
case STATUS_INTEGER_OVERFLOW: sig = SIGFPE; break;
|
||||
|
||||
case STATUS_ILLEGAL_INSTRUCTION:
|
||||
case STATUS_PRIVILEGED_INSTRUCTION:
|
||||
case STATUS_NONCONTINUABLE_EXCEPTION: sig = SIGILL; break;
|
||||
|
||||
case CR_EXCEPTION_TIMEOUT: sig = SIGPROF; break;
|
||||
|
||||
case STATUS_ACCESS_VIOLATION:
|
||||
case STATUS_DATATYPE_MISALIGNMENT:
|
||||
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
||||
case STATUS_GUARD_PAGE_VIOLATION:
|
||||
case STATUS_IN_PAGE_ERROR:
|
||||
case STATUS_NO_MEMORY:
|
||||
case STATUS_INVALID_DISPOSITION:
|
||||
case STATUS_STACK_OVERFLOW: sig = SIGSEGV; break;
|
||||
|
||||
case STATUS_CONTROL_C_EXIT: sig = SIGINT; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
*status = sig ? sig : exit_code << 8;
|
||||
#else
|
||||
waitpid(handle->pid, status, 0);
|
||||
#endif
|
||||
|
|
|
@ -25,8 +25,16 @@
|
|||
# define COMPAT_PROCESS_H_
|
||||
|
||||
# include "criterion/types.h"
|
||||
# include "internal.h"
|
||||
|
||||
struct proc_handle {
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE handle;
|
||||
#else
|
||||
pid_t pid;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct proc_handle;
|
||||
typedef struct proc_handle s_proc_handle;
|
||||
|
||||
struct worker_context {
|
||||
|
|
|
@ -208,13 +208,92 @@ static INLINE bool is_disabled(struct criterion_test *t,
|
|||
|
||||
#define push_event(Kind, ...) \
|
||||
do { \
|
||||
stat_push_event(stats, \
|
||||
suite_stats, \
|
||||
test_stats, \
|
||||
stat_push_event(ctx->stats, \
|
||||
ctx->suite_stats, \
|
||||
ctx->test_stats, \
|
||||
&(struct event) { .kind = Kind, __VA_ARGS__ }); \
|
||||
report(Kind, test_stats); \
|
||||
report(Kind, ctx->test_stats); \
|
||||
} while (0)
|
||||
|
||||
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) {
|
||||
|
||||
struct worker_status *ws = ev->data;
|
||||
struct process_status status = ws->status;
|
||||
|
||||
if (status.kind == SIGNAL) {
|
||||
if (status.status == SIGPROF) {
|
||||
ctx->test_stats->timed_out = true;
|
||||
double elapsed_time = ctx->test->data->timeout;
|
||||
if (elapsed_time == 0 && ctx->suite->data)
|
||||
elapsed_time = ctx->suite->data->timeout;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
push_event(POST_FINI);
|
||||
log(test_timeout, ctx->test_stats);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->normal_finish || !ctx->test_started) {
|
||||
log(other_crash, ctx->test_stats);
|
||||
if (!ctx->test_started) {
|
||||
stat_push_event(ctx->stats,
|
||||
ctx->suite_stats,
|
||||
ctx->test_stats,
|
||||
&(struct event) { .kind = TEST_CRASH });
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx->test_stats->signal = status.status;
|
||||
if (ctx->test->data->signal == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
log(test_crash, ctx->test_stats);
|
||||
} else {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, ctx->test_stats);
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, ctx->test_stats);
|
||||
}
|
||||
} else {
|
||||
if ((ctx->normal_finish && !ctx->cleaned_up) || !ctx->test_started) {
|
||||
log(abnormal_exit, ctx->test_stats);
|
||||
if (!ctx->test_started) {
|
||||
stat_push_event(ctx->stats,
|
||||
ctx->suite_stats,
|
||||
ctx->test_stats,
|
||||
&(struct event) { .kind = TEST_CRASH });
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx->test_stats->exit_code = status.status;
|
||||
if (!ctx->normal_finish) {
|
||||
if (ctx->test->data->exit_code == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
log(abnormal_exit, ctx->test_stats);
|
||||
} else {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, ctx->test_stats);
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, ctx->test_stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void run_test(struct criterion_global_stats *stats,
|
||||
struct criterion_suite_stats *suite_stats,
|
||||
struct criterion_test *test,
|
||||
|
@ -231,16 +310,21 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
proc = spawn_test_worker(test, suite, run_test_child);
|
||||
proc = spawn_test_worker(test, suite, run_test_child, g_worker_pipe);
|
||||
if (proc == NULL && !is_runner())
|
||||
goto cleanup;
|
||||
|
||||
bool test_started = false;
|
||||
bool normal_finish = false;
|
||||
bool cleaned_up = false;
|
||||
struct execution_context ctx = {
|
||||
.stats = stats,
|
||||
.test = test,
|
||||
.test_stats = test_stats,
|
||||
.suite = suite,
|
||||
.suite_stats = suite_stats,
|
||||
};
|
||||
struct event *ev;
|
||||
while ((ev = worker_read_event(proc)) != NULL) {
|
||||
stat_push_event(stats, suite_stats, test_stats, ev);
|
||||
if (ev->kind < WORKER_TERMINATED)
|
||||
stat_push_event(stats, suite_stats, test_stats, ev);
|
||||
switch (ev->kind) {
|
||||
case PRE_INIT:
|
||||
report(PRE_INIT, test);
|
||||
|
@ -249,7 +333,7 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
case PRE_TEST:
|
||||
report(PRE_TEST, test);
|
||||
log(pre_test, test);
|
||||
test_started = true;
|
||||
ctx.test_started = true;
|
||||
break;
|
||||
case THEORY_FAIL: {
|
||||
struct criterion_theory_stats ths = {
|
||||
|
@ -266,77 +350,21 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
case POST_TEST:
|
||||
report(POST_TEST, test_stats);
|
||||
log(post_test, test_stats);
|
||||
normal_finish = true;
|
||||
ctx.normal_finish = true;
|
||||
break;
|
||||
case POST_FINI:
|
||||
report(POST_FINI, test_stats);
|
||||
log(post_fini, test_stats);
|
||||
cleaned_up = true;
|
||||
ctx.cleaned_up = true;
|
||||
break;
|
||||
case WORKER_TERMINATED:
|
||||
handle_worker_terminated(ev, &ctx);
|
||||
sfree(ev);
|
||||
goto cleanup;
|
||||
}
|
||||
sfree(ev);
|
||||
}
|
||||
|
||||
struct process_status status = wait_proc(proc);
|
||||
if (status.kind == SIGNAL) {
|
||||
if (status.status == SIGPROF) {
|
||||
test_stats->timed_out = true;
|
||||
double elapsed_time = test->data->timeout;
|
||||
if (elapsed_time == 0 && suite->data)
|
||||
elapsed_time = suite->data->timeout;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
push_event(POST_FINI);
|
||||
log(test_timeout, test_stats);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (normal_finish || !test_started) {
|
||||
log(other_crash, test_stats);
|
||||
if (!test_started) {
|
||||
stat_push_event(stats,
|
||||
suite_stats,
|
||||
test_stats,
|
||||
&(struct event) { .kind = TEST_CRASH });
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
test_stats->signal = status.status;
|
||||
if (test->data->signal == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
log(test_crash, test_stats);
|
||||
} else {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, test_stats);
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, test_stats);
|
||||
}
|
||||
} else {
|
||||
if ((normal_finish && !cleaned_up) || !test_started) {
|
||||
log(abnormal_exit, test_stats);
|
||||
if (!test_started) {
|
||||
stat_push_event(stats,
|
||||
suite_stats,
|
||||
test_stats,
|
||||
&(struct event) { .kind = TEST_CRASH });
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
test_stats->exit_code = status.status;
|
||||
if (!normal_finish) {
|
||||
if (test->data->exit_code == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
log(abnormal_exit, test_stats);
|
||||
} else {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, test_stats);
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, test_stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
sfree(test_stats);
|
||||
sfree(proc);
|
||||
|
@ -381,6 +409,10 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) {
|
|||
|
||||
fflush(NULL); // flush everything before forking
|
||||
|
||||
g_worker_pipe = stdpipe();
|
||||
if (g_worker_pipe == NULL)
|
||||
abort();
|
||||
|
||||
struct criterion_global_stats *stats = stats_init();
|
||||
map_tests(set, stats, run_test);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
# define CRITERION_RUNNER_H_
|
||||
|
||||
# include "criterion/types.h"
|
||||
# include "compat/pipe.h"
|
||||
|
||||
DECL_SECTION_LIMITS(struct criterion_test, cr_tst);
|
||||
DECL_SECTION_LIMITS(struct criterion_suite, cr_sts);
|
||||
|
@ -41,4 +42,6 @@ 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_ */
|
||||
|
|
|
@ -63,7 +63,7 @@ struct event *worker_read_event(struct process *proc) {
|
|||
|
||||
void run_worker(struct worker_context *ctx) {
|
||||
cr_redirect_stdin();
|
||||
g_event_pipe = pipe_out(ctx->pipe, 1);
|
||||
g_event_pipe = pipe_out(ctx->pipe, PIPE_CLOSE);
|
||||
|
||||
ctx->func(ctx->test, ctx->suite);
|
||||
fclose(g_event_pipe);
|
||||
|
@ -76,11 +76,8 @@ void run_worker(struct worker_context *ctx) {
|
|||
|
||||
struct process *spawn_test_worker(struct criterion_test *test,
|
||||
struct criterion_suite *suite,
|
||||
f_worker_func func) {
|
||||
s_pipe_handle *pipe = stdpipe();
|
||||
if (pipe == NULL)
|
||||
abort();
|
||||
|
||||
f_worker_func func,
|
||||
s_pipe_handle *pipe) {
|
||||
g_worker_context = (struct worker_context) {
|
||||
.test = test,
|
||||
.suite = suite,
|
||||
|
@ -92,10 +89,10 @@ struct process *spawn_test_worker(struct criterion_test *test,
|
|||
|
||||
s_proc_handle *proc = fork_process();
|
||||
if (proc == (void *) -1) {
|
||||
goto cleanup;
|
||||
return NULL;
|
||||
} else if (proc == NULL) {
|
||||
run_worker(&g_worker_context);
|
||||
goto cleanup;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = smalloc(
|
||||
|
@ -103,16 +100,11 @@ struct process *spawn_test_worker(struct criterion_test *test,
|
|||
.kind = UNIQUE,
|
||||
.dtor = close_process);
|
||||
|
||||
*ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, 1) };
|
||||
cleanup:
|
||||
sfree(pipe);
|
||||
*ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, PIPE_DUP) };
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct process_status wait_proc(struct process *proc) {
|
||||
int status;
|
||||
wait_process(proc->proc, &status);
|
||||
|
||||
struct process_status get_status(int status) {
|
||||
if (WIFEXITED(status))
|
||||
return (struct process_status) {
|
||||
.kind = EXIT_STATUS,
|
||||
|
@ -127,3 +119,10 @@ struct process_status wait_proc(struct process *proc) {
|
|||
|
||||
return (struct process_status) { .kind = STOPPED };
|
||||
}
|
||||
|
||||
struct process_status wait_proc(struct process *proc) {
|
||||
int status;
|
||||
wait_process(proc->proc, &status);
|
||||
|
||||
return get_status(status);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
# define PROCESS_H_
|
||||
|
||||
# include <stdbool.h>
|
||||
# include "criterion/types.h"
|
||||
# include "compat/process.h"
|
||||
# include "compat/pipe.h"
|
||||
|
||||
struct process;
|
||||
|
||||
|
@ -40,14 +42,21 @@ struct process_status {
|
|||
int status;
|
||||
};
|
||||
|
||||
struct worker_status {
|
||||
s_proc_handle proc;
|
||||
struct process_status status;
|
||||
};
|
||||
|
||||
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,
|
||||
void (*func)(struct criterion_test *, struct criterion_suite *));
|
||||
f_worker_func func,
|
||||
s_pipe_handle *pipe);
|
||||
struct event *worker_read_event(struct process *proc);
|
||||
|
||||
#endif /* !PROCESS_H_ */
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "criterion/stats.h"
|
||||
#include "criterion/common.h"
|
||||
#include "criterion/hooks.h"
|
||||
#include "core/worker.h"
|
||||
#include "event.h"
|
||||
|
||||
FILE *g_event_pipe = NULL;
|
||||
|
@ -112,6 +113,20 @@ fail_assert:
|
|||
*ev = (struct event) { .kind = kind, .data = elapsed_time };
|
||||
return ev;
|
||||
}
|
||||
case WORKER_TERMINATED: {
|
||||
struct worker_status *status = malloc(sizeof (struct worker_status));
|
||||
if (fread(status, sizeof (struct worker_status), 1, f) == 0) {
|
||||
free(status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct event *ev = smalloc(
|
||||
.size = sizeof (struct event),
|
||||
.dtor = destroy_event
|
||||
);
|
||||
*ev = (struct event) { .kind = kind, .data = status };
|
||||
return ev;
|
||||
}
|
||||
default: {
|
||||
struct event *ev = smalloc(sizeof (struct event));
|
||||
*ev = (struct event) { .kind = kind, .data = NULL };
|
||||
|
|
|
@ -34,6 +34,10 @@ struct event {
|
|||
void *data;
|
||||
};
|
||||
|
||||
enum other_event_kinds {
|
||||
WORKER_TERMINATED = 1 << 30,
|
||||
};
|
||||
|
||||
struct event *read_event(FILE *f);
|
||||
|
||||
#endif /* !EVENT_H_ */
|
||||
|
|
Loading…
Add table
Reference in a new issue