`poll()` returns -1 but errno is 0

Nahor via Cygwin <[email protected]> Thu, 16 Apr 2026 15:37:45 -0700
Newsgroups gmane.os.cygwin
Message-ID <CA+1R0Vh-hygR06K4MUbvou8JQwGP4B5YvMCc0rJiQUYzp2kAGg@mail.gmail.com>
Hi,

In some cases, `poll()` return -1 but errno is still 0.
Attached is a way to reproduce it. On my system, it fails immediately
(from a wall clock point of view, there are quite a few iterations
with the app).

This was first reported against the fish shell and then against MSYS2
(https://github.com/msys2/msys2-runtime/issues/308). The MSYS2 issue
also has a reproducible sample. It's not as quick to fail, but it's
less artificial (more similar to how fish works).

Potentially related:
Sometimes `poll()` fails with a EFAULT errno. I haven't found a way to
reproduce that outside the fish test suite. And there, it's fairly
rare (maybe 5% of the time for the full test suite, which has ~150
tests, some of which spawn lots of processes)

Regards,
Nahor


-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple
main.c (text/plain, 2.2 KB)
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define FD_MAX_COUNT 100
static pthread_mutex_t monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
static int pending_fds[FD_MAX_COUNT];

int make_nonblock(int fd) {
  int flags = fcntl(fd, F_GETFL, 0);
  assert(flags >= 0);
  if ((flags & O_NONBLOCK) != 0) {
    assert(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
  }
}

void make_pipe(int fds[2]) {
  assert(pipe(fds) == 0);

  make_nonblock(fds[0]);
  make_nonblock(fds[1]);
}

void* monitor_thread(void *) {
  while (1) {
    struct pollfd pfds[FD_MAX_COUNT];
    int pfds_count = 0;
    pthread_mutex_lock(&monitor_mutex);
    for (int i = 0; i < FD_MAX_COUNT; ++i) {
      if (pending_fds[i] >= 0) {
        pfds[pfds_count].fd = pending_fds[i];
        pfds[pfds_count].events = POLLIN;
        pfds[pfds_count].revents = 0;
        pfds_count++;
      }
    }
    pthread_mutex_unlock(&monitor_mutex);

    int res = poll(pfds, pfds_count, 0);
    if (res < 0) {
        if (errno != EBADF) {
          perror("poll");
          exit(1);
        }
    }
  }

  return NULL;
}

void* client_thread(void *_idx) {
  size_t idx = (size_t)_idx;

  while (1) {
    // sleep
    int duration = rand() % 100;
    usleep(duration * 1000);

    // create fd
    int fds[2];
    make_pipe(fds);

    // store fd for poll
    pthread_mutex_lock(&monitor_mutex);
    pending_fds[idx] = fds[0];
    pthread_mutex_unlock(&monitor_mutex);

    // sleep
    duration = rand() % 100;
    usleep(duration * 1000);

    // remove fd from poll and close
    pthread_mutex_lock(&monitor_mutex);
    pending_fds[idx] = -1;
    pthread_mutex_unlock(&monitor_mutex);
    close(fds[0]);
    close(fds[1]);
  }

  return NULL;
}

int main() {
  for (int i = 0; i < FD_MAX_COUNT; ++i) {
    pending_fds[i] = -1;
  }
  pthread_t threads[FD_MAX_COUNT];
  for (size_t i = 0; i < FD_MAX_COUNT; ++i) {
    pthread_create(&threads[i], NULL, client_thread, (void*)i);
  }
  pthread_t monitor_th;
  pthread_create(&monitor_th, NULL, monitor_thread, NULL);

  // not actually returning
  pthread_join(monitor_th, NULL);
  for (size_t i = 0; i < FD_MAX_COUNT; ++i) {
    pthread_join(threads[i], NULL);
  }
}