]>
Commit | Line | Data |
---|---|---|
964d3577 A |
1 | #include <errno.h> |
2 | #include <pthread.h> | |
3 | #include <stdio.h> | |
4 | #include <unistd.h> | |
964d3577 | 5 | #include <sys/wait.h> |
2546420a | 6 | #include <stdlib.h> |
964d3577 | 7 | |
a0619f9c | 8 | #include "darwintest_defaults.h" |
2546420a A |
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); | |
964d3577 A |
33 | } |
34 | ||
2546420a A |
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++; | |
964d3577 | 68 | } |
2546420a A |
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"); | |
964d3577 | 154 | } |
964d3577 | 155 | } |