In forkrun(), fix SIGCHLD waiting and test it

Spotted by Thomas Gleixner <tglx@linutronix.de>, THANK YOU

forkrun() never properly handled the case that a child exited before
the timeout expired, because we had failed to block the signal and
hence never received a SIGCHLD. This was overlooked because this
functionality was never relevant (it only delayed test execution) and
because we did not explicitly test it.

Related to #31
parent dc565b27
......@@ -50,6 +50,7 @@
#include "fellow_hash.h"
#include "fellow_testenv.h"
typedef void child_f(void);
typedef void t_f(struct fellow_fd *ffd, void *t_priv);
typedef void t_resur_init_f(void **priv);
......@@ -57,6 +58,7 @@ typedef void t_resur_fini_f(void **priv);
struct tc {
const char * const name;
child_f *child_f;
t_f *t_f;
fellow_resurrect_f *resur_f;
t_resur_init_f *resur_init_f;
......@@ -78,6 +80,20 @@ randalloc(buddy_t *buddy, double max)
return (e);
}
/* ----------------------------------------------------------------------
* simply exit from child
*/
static void v_matchproto_(child_f)
t_exit(void)
{
exit(0);
}
const struct tc tc_SIGCHLD = {
.name = "tc_SIGCHLD - just exit, test for forkrun()",
.child_f = t_exit
};
/* ----------------------------------------------------------------------
* test log append
*/
......@@ -668,6 +684,9 @@ run(const struct tc *tc, void *t_priv)
unsigned l, h;
int r = 0;
if (tc->child_f)
tc->child_f();
#ifdef HAVE_XXHASH_H
l = FH_LIM;
#else
......@@ -687,9 +706,15 @@ run(const struct tc *tc, void *t_priv)
int
forkrun(const struct tc *tc, double killafter, void *t_priv)
{
struct timespec timeo;
sigset_t mask;
pid_t pid;
int i;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL);
pid = fork();
assert(pid != -1);
if (pid == 0)
......@@ -700,29 +725,29 @@ forkrun(const struct tc *tc, double killafter, void *t_priv)
assert(pid != -1);
if (! WIFEXITED(i) || WEXITSTATUS(i) != 0)
exit(i);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
return (WEXITSTATUS(i));
}
// idea from https://stackoverflow.com/a/20173592
sigset_t mask;
struct timespec timeo;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
// from vtim.c
timeo.tv_sec = (time_t)trunc(killafter);
timeo.tv_nsec = (int)(1e9 * (killafter - timeo.tv_sec));
DBG("killtimer %f", killafter);
i = sigtimedwait(&mask, NULL, &timeo);
if (i < 0) {
if (tc == &tc_SIGCHLD)
assert(i == SIGCHLD); // self test
else if (i < 0) {
// XXX WHY EINTR? #31
assert(errno == EAGAIN || errno == EINTR);
i = kill(pid, SIGABRT);
assert(i == 0 || i == ESRCH);
}
} else
assert(i == SIGCHLD);
(void) waitpid(pid, &i, 0);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
return (0);
}
......@@ -763,6 +788,9 @@ main(int argc, char **argv)
if (argc == 2 && *argv[1] != '-')
filename = argv[1];
// test forkrun SIGCHLD
AZ(forkrun(&tc_SIGCHLD, 1.0, 0));
AZ(run(&tc_adddel_netzero, 0));
AZ(run(&tc_log_append, 0));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment