1#include "test/jemalloc_test.h"
2
3#ifndef _WIN32
4#include <sys/wait.h>
5#endif
6
7#ifndef _WIN32
8static void
9wait_for_child_exit(int pid) {
10	int status;
11	while (true) {
12		if (waitpid(pid, &status, 0) == -1) {
13			test_fail("Unexpected waitpid() failure.");
14		}
15		if (WIFSIGNALED(status)) {
16			test_fail("Unexpected child termination due to "
17			    "signal %d", WTERMSIG(status));
18			break;
19		}
20		if (WIFEXITED(status)) {
21			if (WEXITSTATUS(status) != 0) {
22				test_fail("Unexpected child exit value %d",
23				    WEXITSTATUS(status));
24			}
25			break;
26		}
27	}
28}
29#endif
30
31TEST_BEGIN(test_fork) {
32#ifndef _WIN32
33	void *p;
34	pid_t pid;
35
36	/* Set up a manually managed arena for test. */
37	unsigned arena_ind;
38	size_t sz = sizeof(unsigned);
39	assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
40	    0, "Unexpected mallctl() failure");
41
42	/* Migrate to the new arena. */
43	unsigned old_arena_ind;
44	sz = sizeof(old_arena_ind);
45	assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
46	    (void *)&arena_ind, sizeof(arena_ind)), 0,
47	    "Unexpected mallctl() failure");
48
49	p = malloc(1);
50	assert_ptr_not_null(p, "Unexpected malloc() failure");
51
52	pid = fork();
53
54	free(p);
55
56	p = malloc(64);
57	assert_ptr_not_null(p, "Unexpected malloc() failure");
58	free(p);
59
60	if (pid == -1) {
61		/* Error. */
62		test_fail("Unexpected fork() failure");
63	} else if (pid == 0) {
64		/* Child. */
65		_exit(0);
66	} else {
67		wait_for_child_exit(pid);
68	}
69#else
70	test_skip("fork(2) is irrelevant to Windows");
71#endif
72}
73TEST_END
74
75#ifndef _WIN32
76static void *
77do_fork_thd(void *arg) {
78	malloc(1);
79	int pid = fork();
80	if (pid == -1) {
81		/* Error. */
82		test_fail("Unexpected fork() failure");
83	} else if (pid == 0) {
84		/* Child. */
85		char *args[] = {"true", NULL};
86		execvp(args[0], args);
87		test_fail("Exec failed");
88	} else {
89		/* Parent */
90		wait_for_child_exit(pid);
91	}
92	return NULL;
93}
94#endif
95
96#ifndef _WIN32
97static void
98do_test_fork_multithreaded() {
99	thd_t child;
100	thd_create(&child, do_fork_thd, NULL);
101	do_fork_thd(NULL);
102	thd_join(child, NULL);
103}
104#endif
105
106TEST_BEGIN(test_fork_multithreaded) {
107#ifndef _WIN32
108	/*
109	 * We've seen bugs involving hanging on arenas_lock (though the same
110	 * class of bugs can happen on any mutex).  The bugs are intermittent
111	 * though, so we want to run the test multiple times.  Since we hold the
112	 * arenas lock only early in the process lifetime, we can't just run
113	 * this test in a loop (since, after all the arenas are initialized, we
114	 * won't acquire arenas_lock any further).  We therefore repeat the test
115	 * with multiple processes.
116	 */
117	for (int i = 0; i < 100; i++) {
118		int pid = fork();
119		if (pid == -1) {
120			/* Error. */
121			test_fail("Unexpected fork() failure,");
122		} else if (pid == 0) {
123			/* Child. */
124			do_test_fork_multithreaded();
125			_exit(0);
126		} else {
127			wait_for_child_exit(pid);
128		}
129	}
130#else
131	test_skip("fork(2) is irrelevant to Windows");
132#endif
133}
134TEST_END
135
136int
137main(void) {
138	return test_no_reentrancy(
139	    test_fork,
140	    test_fork_multithreaded);
141}
142