Add `urcu_posix_assert()` as `assert()` replacement
[urcu.git] / tests / regression / test_urcu_fork.c
CommitLineData
390f0a26
MD
1/*
2 * test_urcu_fork.c
3 *
4 * Userspace RCU library - test program (fork)
5 *
6 * Copyright February 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
390f0a26
MD
23#include <stdio.h>
24#include <pthread.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#include <sys/wait.h>
29#include <unistd.h>
30#include <stdio.h>
390f0a26
MD
31#include <sched.h>
32#include <errno.h>
33
01477510 34#include <urcu/assert.h>
390f0a26
MD
35#include <urcu/arch.h>
36#include <urcu/tls-compat.h>
37
38#ifndef DYNAMIC_LINK_TEST
39#define _LGPL_SOURCE
40#else
41#define rcu_debug_yield_read()
42#endif
43#include <urcu.h>
44
ad460058
MD
45#include "tap.h"
46
d6aefcd1
MD
47/* We generate children 3 levels deep */
48#define FORK_DEPTH 3
49/* Each generation spawns 10 children */
50#define NR_FORK 10
51
ad460058
MD
52#define NR_TESTS NR_FORK
53
d6aefcd1
MD
54static int fork_generation;
55
ad460058
MD
56/*
57 * Only print diagnostic for top level parent process, else the console
58 * has trouble formatting the tap output.
59 */
60#define diag_gen0(...) \
61 do { \
62 if (!fork_generation) \
63 diag(__VA_ARGS__); \
64 } while (0)
65
390f0a26
MD
66struct test_node {
67 int somedata;
68 struct rcu_head head;
69};
70
71static void cb(struct rcu_head *head)
72{
73 struct test_node *node;
74
ad460058 75 diag_gen0("rcu callback invoked in pid: %d", (int) getpid());
390f0a26
MD
76 node = caa_container_of(head, struct test_node, head);
77 free(node);
78}
79
80static void test_rcu(void)
81{
82 struct test_node *node;
83
84 rcu_register_thread();
85
86 synchronize_rcu();
87
88 rcu_read_lock();
89 rcu_read_unlock();
90
91 node = malloc(sizeof(*node));
01477510 92 urcu_posix_assert(node);
390f0a26
MD
93
94 call_rcu(&node->head, cb);
95
96 synchronize_rcu();
97
98 rcu_unregister_thread();
99}
100
d6aefcd1
MD
101/*
102 * Return 0 if child, > 0 if parent, < 0 on error.
103 */
104static int do_fork(const char *execname)
390f0a26
MD
105{
106 pid_t pid;
390f0a26 107
ad460058 108 diag_gen0("%s parent pid: %d, before fork",
d6aefcd1 109 execname, (int) getpid());
390f0a26
MD
110
111 call_rcu_before_fork();
112 pid = fork();
390f0a26
MD
113 if (pid == 0) {
114 /* child */
d6aefcd1 115 fork_generation++;
ad460058 116 tap_disable();
d6aefcd1 117
390f0a26 118 call_rcu_after_fork_child();
ad460058 119 diag_gen0("%s child pid: %d, after fork",
d6aefcd1 120 execname, (int) getpid());
390f0a26 121 test_rcu();
ad460058 122 diag_gen0("%s child pid: %d, after rcu test",
d6aefcd1
MD
123 execname, (int) getpid());
124 if (fork_generation >= FORK_DEPTH)
125 exit(EXIT_SUCCESS);
126 return 0;
390f0a26
MD
127 } else if (pid > 0) {
128 int status;
129
130 /* parent */
131 call_rcu_after_fork_parent();
ad460058 132 diag_gen0("%s parent pid: %d, after fork",
d6aefcd1 133 execname, (int) getpid());
390f0a26 134 test_rcu();
ad460058 135 diag_gen0("%s parent pid: %d, after rcu test",
d6aefcd1 136 execname, (int) getpid());
390f0a26
MD
137 for (;;) {
138 pid = wait(&status);
d6aefcd1 139 if (pid < 0) {
ad460058
MD
140 if (!fork_generation)
141 perror("wait");
d6aefcd1
MD
142 return -1;
143 }
390f0a26 144 if (WIFEXITED(status)) {
ad460058 145 diag_gen0("child %u exited normally with status %u",
390f0a26 146 pid, WEXITSTATUS(status));
d6aefcd1
MD
147 if (WEXITSTATUS(status))
148 return -1;
390f0a26
MD
149 break;
150 } else if (WIFSIGNALED(status)) {
ad460058 151 diag_gen0("child %u was terminated by signal %u",
390f0a26 152 pid, WTERMSIG(status));
d6aefcd1 153 return -1;
390f0a26
MD
154 } else {
155 continue;
156 }
157 }
d6aefcd1 158 return 1;
390f0a26 159 } else {
ad460058
MD
160 if (!fork_generation)
161 perror("fork");
d6aefcd1
MD
162 return -1;
163 }
164}
165
70469b43 166int main(int argc __attribute__((unused)), char **argv)
d6aefcd1
MD
167{
168 unsigned int i;
169
ad460058
MD
170 plan_tests(NR_TESTS);
171
d6aefcd1
MD
172#if 0
173 /* pthread_atfork does not work with malloc/free in callbacks */
174 ret = pthread_atfork(call_rcu_before_fork,
175 call_rcu_after_fork_parent,
176 call_rcu_after_fork_child);
177 if (ret) {
178 errno = ret;
179 perror("pthread_atfork");
390f0a26
MD
180 exit(EXIT_FAILURE);
181 }
d6aefcd1
MD
182#endif
183
184restart:
185 for (i = 0; i < NR_FORK; i++) {
186 int ret;
187
188 test_rcu();
189 synchronize_rcu();
190 ret = do_fork(argv[0]);
ad460058
MD
191 if (!fork_generation) {
192 ok(ret >= 0, "child status %d", ret);
193 }
194 if (ret == 0) { /* child */
d6aefcd1 195 goto restart;
ad460058 196 } else if (ret < 0) {
d6aefcd1 197 goto error;
ad460058
MD
198 } else {
199 /* else parent, continue. */
200 }
201 }
202 if (!fork_generation) {
203 return exit_status();
204 } else {
205 exit(EXIT_SUCCESS);
d6aefcd1 206 }
d6aefcd1
MD
207
208error:
ad460058
MD
209 if (!fork_generation) {
210 return exit_status();
211 } else {
212 exit(EXIT_FAILURE);
213 }
390f0a26 214}
This page took 0.043007 seconds and 4 git commands to generate.