/* * Copyright (C) 2008 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Authors: * Mark McLoughlin */ /* gcc -g -Wall -lrt test-posix-timer-race.c -o test-posix-timer-race */ /* See http://lkml.org/lkml/2008/7/16/217 */ #include #include #include #include #include #include #include #include #include #include #include #define TIMER_LEN_US 250L static int signalfd_setup(void) { sigset_t mask; int sigfd; sigemptyset(&mask); sigaddset(&mask, SIGALRM); sigprocmask(SIG_BLOCK, &mask, NULL); sigfd = signalfd(-1, &mask, 0); if (sigfd < 0) err(1, "signalfd()"); if (fcntl(sigfd, F_SETFL, O_NONBLOCK) < 0) err(1, "fcntl()"); return sigfd; } static void read_signalfd(int sigfd) { struct signalfd_siginfo info; while (1) { ssize_t len; do { len = read(sigfd, &info, sizeof(info)); } while (len == -1 && errno == EINTR); if (len == -1) { if (errno == EAGAIN) break; else err(1, "signalfd read()"); } if (len != sizeof(info)) err(1, "signalfd read() returned %zd bytes", len); if (info.ssi_signo != SIGALRM) printf("-"); else if (info.ssi_overrun) printf("+"); else printf("."); fflush(stdout); } } static void set_timer(timer_t timer, int usecs) { struct itimerspec timeout; if (timer_gettime(timer, &timeout) < 0) err(1, "timer_gettime()"); if (timeout.it_value.tv_sec || timeout.it_value.tv_nsec) return; timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ timeout.it_value.tv_sec = usecs / 1000000; timeout.it_value.tv_nsec = (usecs % 1000000) * 1000; if (timer_settime(timer, 0, &timeout, NULL) < 0) err(1, "timer_settime()"); } static void * thread_loop(void *arg) { sigset_t signals; timer_t timer; sigfillset(&signals); sigdelset(&signals, SIGALRM); sigprocmask(SIG_BLOCK, &signals, NULL); if (timer_create(CLOCK_REALTIME, NULL, &timer) < 0) err(1, "timer_create()"); while (1) set_timer(timer, TIMER_LEN_US); timer_delete(timer); return NULL; } int main(int argc, char **argv) { pthread_t thread; int sigfd; sigfd = signalfd_setup(); pthread_create(&thread, NULL, thread_loop, NULL); srand(getpid()); while (1) { fd_set rfds; struct timeval timeout; int ret; FD_ZERO(&rfds); FD_SET(sigfd, &rfds); timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select(sigfd + 1, &rfds, NULL, NULL, &timeout); if (ret < 0) err(1, "select()"); if (ret == 0) { printf("timeout! kill -SIGALRM me please!\n"); continue; } usleep(TIMER_LEN_US * (0.75 + ((rand() % 50) * 0.01))); if (FD_ISSET(sigfd, &rfds)) read_signalfd(sigfd); } pthread_join(thread, NULL); close(sigfd); return 0; }