flock deadlock

Nahor nahor.j+cygwin@gmail.com
Sun Dec 28 19:52:36 GMT 2025


Attached is a reproducible example.
The example just calls `fork()` then open/flock/close a directory and
repeats (fork/open/flock/close). The forks optionally sleep then
open/flock/close the same directory and exit.

There is no issue if either the parent or the children don't call `flock()`.
Without sleeping, the example deadlocks immediately on my system 100%
of the time. Killing the child allow the parent to proceed, fork the
next child, which triggers the next deadlock.
When sleeping, _sometimes_ one child will deadlock with the parent.
Killing that child allows the parent and remaining children to proceed
(and potentially trigger another deadlock). Killing the parent also
unblocks all the children.

Regards,
Nahor.
-------------- next part --------------
// #define SLEEP

#include <stdlib.h>

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

const int FORK_COUNT = 10;

typedef struct {
  int id;
} call_args;

static void *flock_call(void *arg) {
  call_args *args = (call_args *)arg;

#ifdef SLEEP
  if (args->id != -1) {
    struct timespec duration;
    duration.tv_sec = 0;
    duration.tv_nsec = 1;
    nanosleep(&duration, NULL);
  }
#endif

  // open and lock dir
  int dir_fd = open("/cygdrive/c/cygwin64/etc", O_RDONLY, 0);
  if (dir_fd < 0) {
    printf("open dir error: %d - %s\n", errno, strerror(errno));
  }
  assert(dir_fd >= 0);

  // lock
  int lock_res = flock(dir_fd, LOCK_SH);
  if (lock_res != 0) {
    printf("lock error: %d - %s\n", errno, strerror(errno));
  }
  assert(lock_res == 0);

  // close
  assert(close(dir_fd) == 0);

  printf("done[%i]\n", args->id);
  return NULL;
}

int main() {
  call_args main_args;
  main_args.id = -1;

  // spawn children
  pid_t children[FORK_COUNT];
  for (int i = 0; i < FORK_COUNT; ++i) {
    call_args args;
    args.id = i;

    children[i] = fork();
    if (children[i] == -1) {
      perror("fork");
      exit(EXIT_FAILURE);
    }
    if (children[i] == 0) {
      flock_call(&args);
      return 0;
    }
    flock_call(&main_args);
  }

  // wait children
  for (int i = 0; i < FORK_COUNT; ++i) {
    int wstatus;
    pid_t w;
    do {
      w = waitpid(children[i], &wstatus, WUNTRACED | WCONTINUED);
      if (w == -1) {
        perror("waitpid");
        exit(EXIT_FAILURE);
      }
    } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
  }

  printf("done[main]\n");
  return 0;
}


More information about the Cygwin mailing list