]>
git.saurik.com Git - apple/libpthread.git/blob - tests/atfork.c
8 #include "darwintest_defaults.h"
10 static const char ATFORK_PREPARE
[] = "prepare";
11 static const char ATFORK_PARENT
[] = "parent";
12 static const char ATFORK_CHILD
[] = "child";
14 struct callback_event
{
15 size_t registration_idx
;
19 #define NUM_REGISTRATIONS ((size_t) 20)
20 static struct callback_event events
[NUM_REGISTRATIONS
* 5];
21 static size_t recorded_events
= 0;
24 record_callback(size_t registration_idx
, const char *type
)
26 if (recorded_events
== (sizeof(events
) / sizeof(events
[0]))) {
27 return; // events array is full
29 struct callback_event
*evt
= &events
[recorded_events
++];
30 evt
->registration_idx
= registration_idx
;
32 T_LOG("[%d] callback: #%lu %s", getpid(), registration_idx
, type
);
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)
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); }
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 },
51 static void assert_event_sequence(struct callback_event
*sequence
,
52 const char *expected_type
, size_t start_idx
, size_t end_idx
)
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
);
59 if (start_idx
== end_idx
) {
62 if (start_idx
< end_idx
) {
71 static size_t inspect_event_sequence(struct callback_event
*sequence
,
72 const char *expected_type
, size_t start_idx
, size_t end_idx
)
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
);
82 if (start_idx
== end_idx
) {
85 if (start_idx
< end_idx
) {
95 T_DECL(atfork
, "pthread_atfork")
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()");
107 pid
= fork(); // first level fork
110 // don't use ASSERTs/EXPECTs in child processes so not to confuse
113 pid
= fork(); // second level fork
116 T_LOG("FAIL: second fork() failed");
119 if (recorded_events
!= NUM_REGISTRATIONS
* 4) {
120 T_LOG("FAIL: unexpected # of events: %lu instead of %lu",
121 recorded_events
, NUM_REGISTRATIONS
* 4);
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);
129 exit((int) failures
);
133 if (waitpid(pid
, &status
, 0) != pid
) {
134 T_LOG("FAIL: grandchild waitpid failed");
137 if (WEXITSTATUS(status
) != 0) {
138 T_LOG("FAIL: grandchild exited with status %d",
139 WEXITSTATUS(status
));
143 exit(0); // don't run leaks in the child and the grandchild
145 T_ASSERT_GE(pid
, 0, "first fork()");
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);
152 T_ASSERT_EQ(pid
, waitpid(pid
, &status
, 0), "child waitpid");
153 T_ASSERT_POSIX_ZERO(WEXITSTATUS(status
), "child exit status");