]> git.saurik.com Git - apple/libpthread.git/blobdiff - tests/atfork.c
libpthread-416.60.2.tar.gz
[apple/libpthread.git] / tests / atfork.c
index 7b760c0f18ff4f294f3c10da35a710fe2cb2b1a6..a33cf2e67df27ef3999266153e636aba7cd5c7cc 100644 (file)
 #include <pthread.h>
 #include <stdio.h>
 #include <unistd.h>
-#include <os/assumes.h>
 #include <sys/wait.h>
+#include <stdlib.h>
 
-#define DECL_ATFORK(x) \
-static void prepare_##x(void) { \
-       printf("%d: %s\n", getpid(), __FUNCTION__); \
-} \
-static void parent_##x(void) { \
-       printf("%d: %s\n", getpid(), __FUNCTION__); \
-} \
-static void child_##x(void) { \
-       printf("%d: %s\n", getpid(), __FUNCTION__); \
+#include "darwintest_defaults.h"
+
+static const char ATFORK_PREPARE[] = "prepare";
+static const char ATFORK_PARENT[] = "parent";
+static const char ATFORK_CHILD[] = "child";
+
+struct callback_event {
+       size_t registration_idx;
+       const char *type;
+};
+
+#define NUM_REGISTRATIONS ((size_t) 20)
+static struct callback_event events[NUM_REGISTRATIONS * 5];
+static size_t recorded_events = 0;
+
+static void
+record_callback(size_t registration_idx, const char *type)
+{
+       if (recorded_events == (sizeof(events) / sizeof(events[0]))) {
+               return; // events array is full
+       }
+       struct callback_event *evt = &events[recorded_events++];
+       evt->registration_idx = registration_idx;
+       evt->type = type;
+       T_LOG("[%d] callback: #%lu %s", getpid(), registration_idx, type);
 }
 
-#define ATFORK(x) \
-os_assumes_zero(pthread_atfork(prepare_##x, parent_##x, child_##x));
-
-DECL_ATFORK(1);
-DECL_ATFORK(2);
-DECL_ATFORK(3);
-DECL_ATFORK(4);
-DECL_ATFORK(5);
-DECL_ATFORK(6);
-DECL_ATFORK(7);
-DECL_ATFORK(8);
-DECL_ATFORK(9);
-DECL_ATFORK(10);
-DECL_ATFORK(11);
-DECL_ATFORK(12);
-DECL_ATFORK(13);
-DECL_ATFORK(14);
-DECL_ATFORK(15);
-DECL_ATFORK(16);
-DECL_ATFORK(17);
-DECL_ATFORK(18);
-DECL_ATFORK(19);
-
-int main(int argc, char *argv[]) {
-       ATFORK(1);
-       ATFORK(2);
-       ATFORK(3);
-       ATFORK(4);
-       ATFORK(5);
-       ATFORK(6);
-       ATFORK(7);
-       ATFORK(8);
-       ATFORK(9);
-       ATFORK(10);
-       ATFORK(11);
-       ATFORK(12);
-       ATFORK(13);
-       ATFORK(14);
-       ATFORK(15);
-       ATFORK(16);
-       ATFORK(17);
-       ATFORK(18);
-       ATFORK(19);
-
-       pid_t pid = fork();
-       if (pid == 0) {
-               pid = fork(); 
+#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) \
+               X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19)
+
+#define DECLARE_CB(idx) \
+static void cb_prepare_##idx() { record_callback(idx, ATFORK_PREPARE); } \
+static void cb_parent_##idx() { record_callback(idx, ATFORK_PARENT); } \
+static void cb_child_##idx() { record_callback(idx, ATFORK_CHILD); }
+
+TWENTY(DECLARE_CB)
+
+typedef void (*atfork_cb_t)(void);
+static const atfork_cb_t callbacks[NUM_REGISTRATIONS][3] = {
+       #define CB_ELEM(idx) { cb_prepare_##idx, cb_parent_##idx, cb_child_##idx },
+       TWENTY(CB_ELEM)
+};
+
+static void assert_event_sequence(struct callback_event *sequence,
+               const char *expected_type, size_t start_idx, size_t end_idx)
+{
+       while (true) {
+               struct callback_event *evt = &sequence[0];
+               T_QUIET; T_ASSERT_EQ(evt->type, expected_type, NULL);
+               T_QUIET; T_ASSERT_EQ(evt->registration_idx, start_idx, NULL);
+
+               if (start_idx == end_idx) {
+                       break;
+               }
+               if (start_idx < end_idx) {
+                       start_idx++;
+               } else {
+                       start_idx--;
+               }
+               sequence++;
        }
-       if (pid == -1) {
-               posix_assumes_zero(pid);
-       } else if (pid > 0) {
-               int status;
-               posix_assumes_zero(waitpid(pid, &status, 0));
-               posix_assumes_zero(WEXITSTATUS(status));
+}
+
+static size_t inspect_event_sequence(struct callback_event *sequence,
+               const char *expected_type, size_t start_idx, size_t end_idx)
+{
+       size_t failures = 0;
+       while (true) {
+               struct callback_event *evt = &sequence[0];
+               if (evt->type != expected_type || evt->registration_idx != start_idx) {
+                       T_LOG("FAIL: expected {idx, type}: {%lu, %s}. got {%lu, %s}",
+                                 start_idx, expected_type, evt->registration_idx, evt->type);
+                       failures++;
+               }
+               if (start_idx == end_idx) {
+                       break;
+               }
+               if (start_idx < end_idx) {
+                       start_idx++;
+               } else {
+                       start_idx--;
+               }
+               sequence++;
+       }
+       return failures;
+}
+
+T_DECL(atfork, "pthread_atfork")
+{
+       pid_t pid;
+       int status;
+       size_t failures = 0;
+
+       for (size_t i = 0; i < NUM_REGISTRATIONS; i++) {
+               T_QUIET; T_ASSERT_POSIX_ZERO(pthread_atfork(
+                               callbacks[i][0], callbacks[i][1], callbacks[i][2]),
+                               "registering callbacks with pthread_atfork()");
+       }
+
+       pid = fork(); // first level fork
+
+       if (pid == 0) {
+               // don't use ASSERTs/EXPECTs in child processes so not to confuse
+               // darwintest
+
+               pid = fork(); // second level fork
+               
+               if (pid < 0) {
+                       T_LOG("FAIL: second fork() failed");
+                       exit(1);
+               }
+               if (recorded_events != NUM_REGISTRATIONS * 4) {
+                       T_LOG("FAIL: unexpected # of events: %lu instead of %lu",
+                                 recorded_events, NUM_REGISTRATIONS * 4);
+                       exit(1);
+               }
+               failures += inspect_event_sequence(&events[2 * NUM_REGISTRATIONS],
+                               ATFORK_PREPARE, NUM_REGISTRATIONS - 1, 0);
+               failures += inspect_event_sequence(&events[3 * NUM_REGISTRATIONS],
+                               (pid ? ATFORK_PARENT : ATFORK_CHILD), 0, NUM_REGISTRATIONS - 1);
+               if (failures) {
+                       exit((int) failures);
+               }
+
+               if (pid > 0) {
+                       if (waitpid(pid, &status, 0) != pid) {
+                               T_LOG("FAIL: grandchild waitpid failed");
+                               exit(1);
+                       }
+                       if (WEXITSTATUS(status) != 0) {
+                               T_LOG("FAIL: grandchild exited with status %d",
+                                         WEXITSTATUS(status));
+                               exit(1);
+                       }
+               }
+               exit(0); // don't run leaks in the child and the grandchild
+       } else {
+               T_ASSERT_GE(pid, 0, "first fork()");
+
+               T_ASSERT_EQ(recorded_events, NUM_REGISTRATIONS * 2, "# of events");
+               assert_event_sequence(events, ATFORK_PREPARE, NUM_REGISTRATIONS - 1, 0);
+               assert_event_sequence(&events[NUM_REGISTRATIONS],
+                                                         ATFORK_PARENT, 0, NUM_REGISTRATIONS - 1);
+
+               T_ASSERT_EQ(pid, waitpid(pid, &status, 0), "child waitpid");
+               T_ASSERT_POSIX_ZERO(WEXITSTATUS(status), "child exit status");
        }
-       return 0;
 }