X-Git-Url: https://git.liburcu.org/?p=urcu.git;a=blobdiff_plain;f=tests%2Fregression%2Ftest_urcu_fork.c;h=81dbe7742f769fb5b82db701de9cb515c148c60e;hp=ce0c9cafd92c5fe86f6b2edd6ccb3598b4556a06;hb=014775106c60f02818ca755b331f887030bd440f;hpb=f5ab766ee2c8300cb00ca5878b1cb464f960a66d diff --git a/tests/regression/test_urcu_fork.c b/tests/regression/test_urcu_fork.c index ce0c9ca..81dbe77 100644 --- a/tests/regression/test_urcu_fork.c +++ b/tests/regression/test_urcu_fork.c @@ -20,8 +20,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE -#include "config.h" #include #include #include @@ -30,10 +28,10 @@ #include #include #include -#include #include #include +#include #include #include @@ -44,6 +42,27 @@ #endif #include +#include "tap.h" + +/* We generate children 3 levels deep */ +#define FORK_DEPTH 3 +/* Each generation spawns 10 children */ +#define NR_FORK 10 + +#define NR_TESTS NR_FORK + +static int fork_generation; + +/* + * Only print diagnostic for top level parent process, else the console + * has trouble formatting the tap output. + */ +#define diag_gen0(...) \ + do { \ + if (!fork_generation) \ + diag(__VA_ARGS__); \ + } while (0) + struct test_node { int somedata; struct rcu_head head; @@ -53,8 +72,7 @@ static void cb(struct rcu_head *head) { struct test_node *node; - fprintf(stderr, "rcu callback invoked in pid: %d\n", - (int) getpid()); + diag_gen0("rcu callback invoked in pid: %d", (int) getpid()); node = caa_container_of(head, struct test_node, head); free(node); } @@ -71,7 +89,7 @@ static void test_rcu(void) rcu_read_unlock(); node = malloc(sizeof(*node)); - assert(node); + urcu_posix_assert(node); call_rcu(&node->head, cb); @@ -80,68 +98,117 @@ static void test_rcu(void) rcu_unregister_thread(); } -int main(int argc, char **argv) +/* + * Return 0 if child, > 0 if parent, < 0 on error. + */ +static int do_fork(const char *execname) { pid_t pid; - int ret; - -#if 0 - /* pthread_atfork does not work with malloc/free in callbacks */ - ret = pthread_atfork(call_rcu_before_fork, - call_rcu_after_fork_parent, - call_rcu_after_fork_child); - if (ret) { - errno = ret; - perror("pthread_atfork"); - exit(EXIT_FAILURE); - } -#endif - test_rcu(); - - synchronize_rcu(); - - fprintf(stderr, "%s parent pid: %d, before fork\n", - argv[0], (int) getpid()); + diag_gen0("%s parent pid: %d, before fork", + execname, (int) getpid()); call_rcu_before_fork(); pid = fork(); - if (pid == 0) { /* child */ + fork_generation++; + tap_disable(); + call_rcu_after_fork_child(); - fprintf(stderr, "%s child pid: %d, after fork\n", - argv[0], (int) getpid()); + diag_gen0("%s child pid: %d, after fork", + execname, (int) getpid()); test_rcu(); - fprintf(stderr, "%s child pid: %d, after rcu test\n", - argv[0], (int) getpid()); + diag_gen0("%s child pid: %d, after rcu test", + execname, (int) getpid()); + if (fork_generation >= FORK_DEPTH) + exit(EXIT_SUCCESS); + return 0; } else if (pid > 0) { int status; /* parent */ call_rcu_after_fork_parent(); - fprintf(stderr, "%s parent pid: %d, after fork\n", - argv[0], (int) getpid()); + diag_gen0("%s parent pid: %d, after fork", + execname, (int) getpid()); test_rcu(); - fprintf(stderr, "%s parent pid: %d, after rcu test\n", - argv[0], (int) getpid()); + diag_gen0("%s parent pid: %d, after rcu test", + execname, (int) getpid()); for (;;) { pid = wait(&status); + if (pid < 0) { + if (!fork_generation) + perror("wait"); + return -1; + } if (WIFEXITED(status)) { - fprintf(stderr, "child %u exited normally with status %u\n", + diag_gen0("child %u exited normally with status %u", pid, WEXITSTATUS(status)); + if (WEXITSTATUS(status)) + return -1; break; } else if (WIFSIGNALED(status)) { - fprintf(stderr, "child %u was terminated by signal %u\n", + diag_gen0("child %u was terminated by signal %u", pid, WTERMSIG(status)); - break; + return -1; } else { continue; } } + return 1; + } else { + if (!fork_generation) + perror("fork"); + return -1; + } +} + +int main(int argc __attribute__((unused)), char **argv) +{ + unsigned int i; + + plan_tests(NR_TESTS); + +#if 0 + /* pthread_atfork does not work with malloc/free in callbacks */ + ret = pthread_atfork(call_rcu_before_fork, + call_rcu_after_fork_parent, + call_rcu_after_fork_child); + if (ret) { + errno = ret; + perror("pthread_atfork"); + exit(EXIT_FAILURE); + } +#endif + +restart: + for (i = 0; i < NR_FORK; i++) { + int ret; + + test_rcu(); + synchronize_rcu(); + ret = do_fork(argv[0]); + if (!fork_generation) { + ok(ret >= 0, "child status %d", ret); + } + if (ret == 0) { /* child */ + goto restart; + } else if (ret < 0) { + goto error; + } else { + /* else parent, continue. */ + } + } + if (!fork_generation) { + return exit_status(); + } else { + exit(EXIT_SUCCESS); + } + +error: + if (!fork_generation) { + return exit_status(); } else { - perror("fork"); exit(EXIT_FAILURE); } - exit(EXIT_SUCCESS); }