]> git.saurik.com Git - apple/libpthread.git/blob - tests/atfork.c
libpthread-301.20.1.tar.gz
[apple/libpthread.git] / tests / atfork.c
1 #include <errno.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <sys/wait.h>
6 #include <stdlib.h>
7
8 #include "darwintest_defaults.h"
9
10 static const char ATFORK_PREPARE[] = "prepare";
11 static const char ATFORK_PARENT[] = "parent";
12 static const char ATFORK_CHILD[] = "child";
13
14 struct callback_event {
15 size_t registration_idx;
16 const char *type;
17 };
18
19 #define NUM_REGISTRATIONS ((size_t) 20)
20 static struct callback_event events[NUM_REGISTRATIONS * 5];
21 static size_t recorded_events = 0;
22
23 static void
24 record_callback(size_t registration_idx, const char *type)
25 {
26 if (recorded_events == (sizeof(events) / sizeof(events[0]))) {
27 return; // events array is full
28 }
29 struct callback_event *evt = &events[recorded_events++];
30 evt->registration_idx = registration_idx;
31 evt->type = type;
32 T_LOG("[%d] callback: #%lu %s", getpid(), registration_idx, type);
33 }
34
35 #define TWENTY(X) X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) \
36 X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19)
37
38 #define DECLARE_CB(idx) \
39 static void cb_prepare_##idx() { record_callback(idx, ATFORK_PREPARE); } \
40 static void cb_parent_##idx() { record_callback(idx, ATFORK_PARENT); } \
41 static void cb_child_##idx() { record_callback(idx, ATFORK_CHILD); }
42
43 TWENTY(DECLARE_CB)
44
45 typedef void (*atfork_cb_t)(void);
46 static const atfork_cb_t callbacks[NUM_REGISTRATIONS][3] = {
47 #define CB_ELEM(idx) { cb_prepare_##idx, cb_parent_##idx, cb_child_##idx },
48 TWENTY(CB_ELEM)
49 };
50
51 static void assert_event_sequence(struct callback_event *sequence,
52 const char *expected_type, size_t start_idx, size_t end_idx)
53 {
54 while (true) {
55 struct callback_event *evt = &sequence[0];
56 T_QUIET; T_ASSERT_EQ(evt->type, expected_type, NULL);
57 T_QUIET; T_ASSERT_EQ(evt->registration_idx, start_idx, NULL);
58
59 if (start_idx == end_idx) {
60 break;
61 }
62 if (start_idx < end_idx) {
63 start_idx++;
64 } else {
65 start_idx--;
66 }
67 sequence++;
68 }
69 }
70
71 static size_t inspect_event_sequence(struct callback_event *sequence,
72 const char *expected_type, size_t start_idx, size_t end_idx)
73 {
74 size_t failures = 0;
75 while (true) {
76 struct callback_event *evt = &sequence[0];
77 if (evt->type != expected_type || evt->registration_idx != start_idx) {
78 T_LOG("FAIL: expected {idx, type}: {%lu, %s}. got {%lu, %s}",
79 start_idx, expected_type, evt->registration_idx, evt->type);
80 failures++;
81 }
82 if (start_idx == end_idx) {
83 break;
84 }
85 if (start_idx < end_idx) {
86 start_idx++;
87 } else {
88 start_idx--;
89 }
90 sequence++;
91 }
92 return failures;
93 }
94
95 T_DECL(atfork, "pthread_atfork")
96 {
97 pid_t pid;
98 int status;
99 size_t failures = 0;
100
101 for (size_t i = 0; i < NUM_REGISTRATIONS; i++) {
102 T_QUIET; T_ASSERT_POSIX_ZERO(pthread_atfork(
103 callbacks[i][0], callbacks[i][1], callbacks[i][2]),
104 "registering callbacks with pthread_atfork()");
105 }
106
107 pid = fork(); // first level fork
108
109 if (pid == 0) {
110 // don't use ASSERTs/EXPECTs in child processes so not to confuse
111 // darwintest
112
113 pid = fork(); // second level fork
114
115 if (pid < 0) {
116 T_LOG("FAIL: second fork() failed");
117 exit(1);
118 }
119 if (recorded_events != NUM_REGISTRATIONS * 4) {
120 T_LOG("FAIL: unexpected # of events: %lu instead of %lu",
121 recorded_events, NUM_REGISTRATIONS * 4);
122 exit(1);
123 }
124 failures += inspect_event_sequence(&events[2 * NUM_REGISTRATIONS],
125 ATFORK_PREPARE, NUM_REGISTRATIONS - 1, 0);
126 failures += inspect_event_sequence(&events[3 * NUM_REGISTRATIONS],
127 (pid ? ATFORK_PARENT : ATFORK_CHILD), 0, NUM_REGISTRATIONS - 1);
128 if (failures) {
129 exit((int) failures);
130 }
131
132 if (pid > 0) {
133 if (waitpid(pid, &status, 0) != pid) {
134 T_LOG("FAIL: grandchild waitpid failed");
135 exit(1);
136 }
137 if (WEXITSTATUS(status) != 0) {
138 T_LOG("FAIL: grandchild exited with status %d",
139 WEXITSTATUS(status));
140 exit(1);
141 }
142 }
143 exit(0); // don't run leaks in the child and the grandchild
144 } else {
145 T_ASSERT_GE(pid, 0, "first fork()");
146
147 T_ASSERT_EQ(recorded_events, NUM_REGISTRATIONS * 2, "# of events");
148 assert_event_sequence(events, ATFORK_PREPARE, NUM_REGISTRATIONS - 1, 0);
149 assert_event_sequence(&events[NUM_REGISTRATIONS],
150 ATFORK_PARENT, 0, NUM_REGISTRATIONS - 1);
151
152 T_ASSERT_EQ(pid, waitpid(pid, &status, 0), "child waitpid");
153 T_ASSERT_POSIX_ZERO(WEXITSTATUS(status), "child exit status");
154 }
155 }