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 9728b48a
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include "fellow_hash.h" #include "fellow_hash.h"
#include "fellow_testenv.h" #include "fellow_testenv.h"
typedef void child_f(void);
typedef void t_f(struct fellow_fd *ffd, void *t_priv); typedef void t_f(struct fellow_fd *ffd, void *t_priv);
typedef void t_resur_init_f(void **priv); typedef void t_resur_init_f(void **priv);
...@@ -57,6 +58,7 @@ typedef void t_resur_fini_f(void **priv); ...@@ -57,6 +58,7 @@ typedef void t_resur_fini_f(void **priv);
struct tc { struct tc {
const char * const name; const char * const name;
child_f *child_f;
t_f *t_f; t_f *t_f;
fellow_resurrect_f *resur_f; fellow_resurrect_f *resur_f;
t_resur_init_f *resur_init_f; t_resur_init_f *resur_init_f;
...@@ -78,6 +80,20 @@ randalloc(buddy_t *buddy, double max) ...@@ -78,6 +80,20 @@ randalloc(buddy_t *buddy, double max)
return (e); 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 * test log append
*/ */
...@@ -668,6 +684,9 @@ run(const struct tc *tc, void *t_priv) ...@@ -668,6 +684,9 @@ run(const struct tc *tc, void *t_priv)
unsigned l, h; unsigned l, h;
int r = 0; int r = 0;
if (tc->child_f)
tc->child_f();
#ifdef HAVE_XXHASH_H #ifdef HAVE_XXHASH_H
l = FH_LIM; l = FH_LIM;
#else #else
...@@ -687,9 +706,15 @@ run(const struct tc *tc, void *t_priv) ...@@ -687,9 +706,15 @@ run(const struct tc *tc, void *t_priv)
int int
forkrun(const struct tc *tc, double killafter, void *t_priv) forkrun(const struct tc *tc, double killafter, void *t_priv)
{ {
struct timespec timeo;
sigset_t mask;
pid_t pid; pid_t pid;
int i; int i;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL);
pid = fork(); pid = fork();
assert(pid != -1); assert(pid != -1);
if (pid == 0) if (pid == 0)
...@@ -700,29 +725,29 @@ forkrun(const struct tc *tc, double killafter, void *t_priv) ...@@ -700,29 +725,29 @@ forkrun(const struct tc *tc, double killafter, void *t_priv)
assert(pid != -1); assert(pid != -1);
if (! WIFEXITED(i) || WEXITSTATUS(i) != 0) if (! WIFEXITED(i) || WEXITSTATUS(i) != 0)
exit(i); exit(i);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
return (WEXITSTATUS(i)); return (WEXITSTATUS(i));
} }
// idea from https://stackoverflow.com/a/20173592
sigset_t mask;
struct timespec timeo;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
// from vtim.c // from vtim.c
timeo.tv_sec = (time_t)trunc(killafter); timeo.tv_sec = (time_t)trunc(killafter);
timeo.tv_nsec = (int)(1e9 * (killafter - timeo.tv_sec)); timeo.tv_nsec = (int)(1e9 * (killafter - timeo.tv_sec));
DBG("killtimer %f", killafter); DBG("killtimer %f", killafter);
i = sigtimedwait(&mask, NULL, &timeo); 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 // XXX WHY EINTR? #31
assert(errno == EAGAIN || errno == EINTR); assert(errno == EAGAIN || errno == EINTR);
i = kill(pid, SIGABRT); i = kill(pid, SIGABRT);
assert(i == 0 || i == ESRCH); assert(i == 0 || i == ESRCH);
} } else
assert(i == SIGCHLD);
(void) waitpid(pid, &i, 0); (void) waitpid(pid, &i, 0);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
return (0); return (0);
} }
...@@ -763,6 +788,9 @@ main(int argc, char **argv) ...@@ -763,6 +788,9 @@ main(int argc, char **argv)
if (argc == 2 && *argv[1] != '-') if (argc == 2 && *argv[1] != '-')
filename = argv[1]; filename = argv[1];
// test forkrun SIGCHLD
AZ(forkrun(&tc_SIGCHLD, 1.0, 0));
AZ(run(&tc_adddel_netzero, 0)); AZ(run(&tc_adddel_netzero, 0));
AZ(run(&tc_log_append, 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