/* * Demonstrate cygserver bug introduced in cygwin-20170324.tar.xz snapshot. Run * without arguments. Test against "cygserver -r 40" to get enough threads. * This is otherwise compatible with default cygserver settings; it uses a * single semaphore set of eight semaphores. * * Output will cease within a few seconds. On older cygserver and non-Cygwin * systems, it will run to completion. */ #include #include #include #include #include #include #include #include #define SEM_KEY 0x631a2c3f #define N_WORKER 16 #define N_SEMA (N_WORKER/2) #define N_CYCLE 1000000 union semun { int val; struct semid_ds *buf; unsigned short *array; }; static int print_every = 1; static int lock(int set, unsigned short sem_num) { struct sembuf op; op.sem_num = sem_num; op.sem_op = -1; op.sem_flg = 0; if (0 > semop(set, &op, 1)) { perror("semop"); return 1; } return 0; } static int unlock(int set, unsigned short sem_num) { struct sembuf op; op.sem_num = sem_num; op.sem_op = 1 ; /* only difference vs. lock() */ op.sem_flg = 0; if (0 > semop(set, &op, 1)) { perror("semop"); return 1; } return 0; } /* In parallel, N_WORKER processes run this function. */ static int do_worker(int ordinal, int set) { int i; printf("start worker %d\n", ordinal); fflush(stdout); for (i = 1; i <= N_CYCLE; i++) { unsigned short s0, s1; /* Pick two non-identical semaphore numbers. */ s0 = random() % N_SEMA; do { s1 = random() % N_SEMA; } while (s0 == s1); /* Lock the lower one first, thereby preventing deadlock. */ if (lock(set, s0 < s1 ? s0 : s1) || lock(set, s0 < s1 ? s1 : s0) || unlock(set, s0) || unlock(set, s1)) return 1; if (i % print_every == 0) { printf("worker %d: %d cycles elapsed\n", ordinal, i); fflush(stdout); } } return 0; } int main(int argc, char **argv) { int status = 1, set, i, child_status; if (argc == 2) print_every = atoi(argv[1]); else if (argc != 1) { fprintf(stderr, "Usage: sema_two [print-every-N]\n"); return status; } puts("semget"); fflush(stdout); set = semget(SEM_KEY, N_SEMA, IPC_CREAT | 0600); if (set == -1) { perror("semget"); return status; } puts("SETVAL"); fflush(stdout); for (i = 0; i < N_SEMA; i++) { union semun s; s.val = 1; if (0 > semctl(set, i, SETVAL, s)) { perror("semctl(SETVAL)"); goto cleanup; } } for (i = 0; i < N_WORKER; i++) { pid_t pid; pid = fork(); switch (pid) { case -1: perror("fork"); goto cleanup; case 0: return do_worker(i, set); } } status = 0; cleanup: while (wait(&child_status) != -1) ; if (errno != ECHILD) { perror("wait"); status = 1; } if (0 > semctl(set, 0, IPC_RMID)) { perror("semtctl(IPC_RMID)"); status = 1; } return status; }