One of the Go developers, Michael Pratt, stumbled over an old PR (50094) when investigating a high amount of timer issues and hangs in the Go HEAD version. The upstream bug report is https://github.com/golang/go/issues/42515.

For now, upstream will work around this issue. But would it be possible to get some traction on this issue? The attached C program is a minimal reproducer. On aarch64, the issue reproduces more often than on amd64.

--
Benny
// $ gcc -pthread kqueue_race.c
// $ ./a.out
// kevent = 0
// Incorrect number of events after 477 calls!

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>

int kq;

void* read_kevent(void* arg) {
  int count = 0;
  while (1) {
    count++;
    
    // Non-blocking.
    struct timespec ts = {};
    struct kevent ev;
    int n = kevent(kq, NULL, 0, &ev, 1, &ts);
    if (n < 0) {
      perror("kevent");
      exit(1);
    } else if (n != 1) {
      printf("kevent = %d\n", n);
      printf("Incorrect number of events after %d calls!\n", count);
      exit(2);
    }
  }
}

int main(int argc, char** argv) {
  int p[2];
  int ret = pipe(p);
  if (ret < 0) {
    perror("pipe");
    return 1;
  }

  kq = kqueue();
  if (kq < 0) {
    perror("kqueue");
    return 1;
  }

  struct kevent ev = {};
  EV_SET(&ev, p[0], EVFILT_READ, EV_ADD, 0, 0, 0);
  ret = kevent(kq, &ev, 1, NULL, 0, NULL);
  if (ret < 0) {
    perror("kevent register");
    return 1;
  }

  // Write to pipe, kevent now ready for read end.
  char c = 0;
  ret = write(p[1], &c, 1);
  if (ret != 1) {
    perror("write");
    return 1;
  }

  pthread_t t1, t2;
  ret = pthread_create(&t1, NULL, read_kevent, NULL);
  if (ret < 0) {
    perror("pthread_create");
    return 1;
  }
  ret = pthread_create(&t2, NULL, read_kevent, NULL);
  if (ret < 0) {
    perror("pthread_create");
    return 1;
  }

  pthread_join(t1, NULL);
  pthread_join(t2, NULL);

  return 0;
}

Reply via email to