Skip to content

[libc] EXPECT_EXIT timeouts on darwin #166059

@bojle

Description

@bojle

Running the EXPECT_EXIT macro (by running _Exit_test for example) on darwin (macos) causes
the test to suspend for a while and later timeout.

The likely cause of this is in the file
ExecuteFunctionUnix.cpp.

The poll struct is populated with a zero .event field. This is fine on linux
but hangs on macos. To test this in isolation, here's a small indepent snippet
recreating the condition just like the invoke_in_subprocess fucntion:

#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
  int pipe_fds[2];
  if (pipe(pipe_fds) == -1) {
    perror("pipe failed");
    return EXIT_FAILURE;
  }

  pid_t pid = fork();
  if (pid == -1) {
    perror("fork failed");
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    return EXIT_FAILURE;
  }

  if (pid == 0) {
    // child
    close(pipe_fds[0]); // Close read end
    // Short delay to ensure the parent has time to enter poll()
    usleep(100000); // 100ms
    printf("Child process exiting, closing pipe write end...\n");
    close(pipe_fds[1]);
    _exit(0);
  }

  // Parent
  close(pipe_fds[1]); // Close write end in parent

  printf("Parent process (PID: %d): Waiting for child (PID: %d) exit via "
         "poll(2) for 5000ms...\n",
         getpid(), pid);

  // LIKELY CAUSE OF ERROR
  // Chage .events to POLLIN and it should work
  struct pollfd poll_fd = {.fd = pipe_fds[0], .events = 0, .revents = 0};

  int timeout_ms = 5000;
  int ret = poll(&poll_fd, 1, timeout_ms);
  if (ret == -1) {
    printf("POLL RESULT: FAILED (-1). errno: %d (%s)\n", errno,
           strerror(errno));
  } else if (ret == 0) {
    printf("POLL RESULT: TIMED OUT (0).\n");
  } else {
    printf(
        "POLL RESULT: SUCCEEDED (%d). revents: 0x%x (Expected POLLHUP 0x%x)\n",
        ret, poll_fd.revents, POLLHUP);
    assert(poll_fd.revents & POLLHUP);
  }

  close(pipe_fds[0]);
  int wstatus;
  if (waitpid(pid, &wstatus, 0) == -1) {
    perror("waitpid failed");
  }
  printf("Parent process finished.\n");
  return 0;
}

Compile this and run: clang file.c -o a; ./a. It hangs and exits via a
timeout. Change the events to be POLLIN and it should succeed.

I couldn't find why this happens precisely. My gut feeling is that macos just
doesn't like an empty events field.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions