tests: Move tap-driver.sh out of the autotools aux directory
[urcu.git] / tests / regression / test_urcu_fork.c
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
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>
31 #include <assert.h>
32 #include <sched.h>
33 #include <errno.h>
34
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
45 #include "tap.h"
46
47 /* We generate children 3 levels deep */
48 #define FORK_DEPTH 3
49 /* Each generation spawns 10 children */
50 #define NR_FORK 10
51
52 #define NR_TESTS NR_FORK
53
54 static int fork_generation;
55
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
66 struct test_node {
67 int somedata;
68 struct rcu_head head;
69 };
70
71 static void cb(struct rcu_head *head)
72 {
73 struct test_node *node;
74
75 diag_gen0("rcu callback invoked in pid: %d", (int) getpid());
76 node = caa_container_of(head, struct test_node, head);
77 free(node);
78 }
79
80 static 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));
92 assert(node);
93
94 call_rcu(&node->head, cb);
95
96 synchronize_rcu();
97
98 rcu_unregister_thread();
99 }
100
101 /*
102 * Return 0 if child, > 0 if parent, < 0 on error.
103 */
104 static int do_fork(const char *execname)
105 {
106 pid_t pid;
107
108 diag_gen0("%s parent pid: %d, before fork",
109 execname, (int) getpid());
110
111 call_rcu_before_fork();
112 pid = fork();
113 if (pid == 0) {
114 /* child */
115 fork_generation++;
116 tap_disable();
117
118 call_rcu_after_fork_child();
119 diag_gen0("%s child pid: %d, after fork",
120 execname, (int) getpid());
121 test_rcu();
122 diag_gen0("%s child pid: %d, after rcu test",
123 execname, (int) getpid());
124 if (fork_generation >= FORK_DEPTH)
125 exit(EXIT_SUCCESS);
126 return 0;
127 } else if (pid > 0) {
128 int status;
129
130 /* parent */
131 call_rcu_after_fork_parent();
132 diag_gen0("%s parent pid: %d, after fork",
133 execname, (int) getpid());
134 test_rcu();
135 diag_gen0("%s parent pid: %d, after rcu test",
136 execname, (int) getpid());
137 for (;;) {
138 pid = wait(&status);
139 if (pid < 0) {
140 if (!fork_generation)
141 perror("wait");
142 return -1;
143 }
144 if (WIFEXITED(status)) {
145 diag_gen0("child %u exited normally with status %u",
146 pid, WEXITSTATUS(status));
147 if (WEXITSTATUS(status))
148 return -1;
149 break;
150 } else if (WIFSIGNALED(status)) {
151 diag_gen0("child %u was terminated by signal %u",
152 pid, WTERMSIG(status));
153 return -1;
154 } else {
155 continue;
156 }
157 }
158 return 1;
159 } else {
160 if (!fork_generation)
161 perror("fork");
162 return -1;
163 }
164 }
165
166 int main(int argc, char **argv)
167 {
168 unsigned int i;
169
170 plan_tests(NR_TESTS);
171
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");
180 exit(EXIT_FAILURE);
181 }
182 #endif
183
184 restart:
185 for (i = 0; i < NR_FORK; i++) {
186 int ret;
187
188 test_rcu();
189 synchronize_rcu();
190 ret = do_fork(argv[0]);
191 if (!fork_generation) {
192 ok(ret >= 0, "child status %d", ret);
193 }
194 if (ret == 0) { /* child */
195 goto restart;
196 } else if (ret < 0) {
197 goto error;
198 } else {
199 /* else parent, continue. */
200 }
201 }
202 if (!fork_generation) {
203 return exit_status();
204 } else {
205 exit(EXIT_SUCCESS);
206 }
207
208 error:
209 if (!fork_generation) {
210 return exit_status();
211 } else {
212 exit(EXIT_FAILURE);
213 }
214 }
This page took 0.03334 seconds and 4 git commands to generate.