/* new_timerfd_errors_test.c Copyright (C) 2007 Google Author: Michael Kerrisk Run various tests against the revised timerfd() implementation. */ /* Link with -lrt */ #define _GNU_SOURCE #include #include #include #if defined(__i386__) #define __NR_timerfd 322 #endif static int timerfd(int ufd, int clockid, int flags, struct itimerspec *utmr, struct itimerspec *outmr) { return syscall(__NR_timerfd, ufd, clockid, flags, utmr, outmr); } #define TFD_TIMER_ABSTIME (1 << 0) /**********************************************************************/ // #include #include #include #include #include #include #include /* * die() is for code that unexpectedly fails. * test_failed() is for tests where we didn't get a failure when we * should have. */ #define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) static int failures = 0; #define test_failed(msg) do { \ fprintf(stderr, "TEST FAILED, because: %s\n", msg); \ failures++; \ } while (0); #define MILLISEC 1000000 /* Expressed in nanonsecs */ static int make_new_timerfd(int clockid, int flags, int vsecs, int vnsecs, int isecs, int insecs) { struct itimerspec utmr; utmr.it_value.tv_sec = vsecs; utmr.it_value.tv_nsec = vnsecs; utmr.it_interval.tv_sec = isecs; utmr.it_interval.tv_nsec = insecs; return timerfd(-1, clockid, flags, &utmr, NULL); } /* make_new_timerfd */ int main(int argc, char *argv[]) { struct itimerspec utmr, outmr; int ufd, ret; struct timespec now; uint64_t ticks; int j; /* * All of the following timer creations should succeed. */ ufd = make_new_timerfd(CLOCK_REALTIME, 0, 1, 0, 1, 0); if (ufd == -1) die("timerfd-REALTIME-flags=0"); if (clock_gettime(CLOCK_REALTIME, &now) == -1) die("clock_gettime"); ufd = make_new_timerfd(CLOCK_REALTIME, TFD_TIMER_ABSTIME, now.tv_sec + 1, now.tv_nsec, 1, 0); if (ufd == -1) die("timerfd-REALTIME-ABSTIME"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, 1, 0, 1, 0); if (ufd == -1) die("timerfd-MONOTONIC-flags=0"); if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) die("clock_gettime"); ufd = make_new_timerfd(CLOCK_MONOTONIC, TFD_TIMER_ABSTIME, now.tv_sec + 1, now.tv_nsec, 1, 0); if (ufd == -1) die("timerfd-MONOTONIC-ABSTIME"); ufd = make_new_timerfd(CLOCK_REALTIME, 0, 0, 0, 0, 0); if (ufd == -1) die("timerfd-MONOTONIC-zero-timer"); /* * Some argument values should not be permitted. */ ufd = make_new_timerfd(-1, 0, 1, 0, 1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-negative-CLOCKID-for-new-timer"); ufd = make_new_timerfd(99, 0, 1, 0, 1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-bad-clockid"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0xff, 1, 0, 1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-invalid-flags"); /* * Non-canonical utmr values should be disallowed. */ ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, -1, 0, 1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-negative-it_value.tv_secs"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, 1, -1, 1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-negative-it_value.tv_nsecs"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, 1, 0, -1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-negative-it_interval.tv_secs"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, 1, 0, 1, -1); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-negative-it_interval.tv_nsecs"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, 1, 2000000000, 1, 0); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-non-canonical-it_value.tv_nsecs"); ufd = make_new_timerfd(CLOCK_MONOTONIC, 0, 1, 0, 1, 2000000000); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-non-canonical-it_interval.tv_nsecs"); /* * At least one of utmr and outmr must be non-NULL. */ ufd = timerfd(-1, CLOCK_MONOTONIC, 0, NULL, NULL); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-NULL-utmtr-plus-NULL-outmr"); /* * When creating a new timer, a non-NULL outmr is meaningless * and disallowed. */ utmr.it_value.tv_sec = 10; utmr.it_value.tv_nsec = 1; utmr.it_interval.tv_sec = 10; utmr.it_interval.tv_nsec = 1; ufd = timerfd(-1, CLOCK_MONOTONIC, 0, &utmr, &outmr); if (!(ufd == -1 && errno == EINVAL)) test_failed("allowed-NULL-outmr-for-new-timer"); /******************************************************************* * Various argument combinations are disallowed when modifying an * existing timer. */ ufd = timerfd(-1, CLOCK_REALTIME, 0, &utmr, NULL); if (ufd == -1) die("timerfd: modify setup"); /* * A clockid cannot be changed for an existing timer; * therefore it is required to be -1. (Note: We could not * require it to be zero, because CLOCK_REALTIME == 0.) */ ret = timerfd(ufd, 0, 0, &utmr, NULL); if (!(ret == -1 && errno == EINVAL)) test_failed("allowed-clockid-not-neg-1-clockid-for-timer-mod"); /* * If ufd >= 0 and utmr is NULL, then we are retrieving the setting * of an existing timer; in this case a non-zero flags value is * meaningless and disallowed. */ ret = timerfd(ufd, -1, TFD_TIMER_ABSTIME, NULL, &outmr); if (!(ret == -1 && errno == EINVAL)) test_failed("allowed-non-zero-flags-with-NULL-utmr"); /* * If ufd is not -1, then it must be a valid timerfd file descriptor. */ ret = timerfd(STDOUT_FILENO, -1, 0, &utmr, NULL); if (!(ret == -1 && errno == EINVAL)) test_failed("allowed-non-timerfd-file-descriptor"); ret = timerfd(99, -1, 0, &utmr, NULL); if (!(ret == -1 && errno == EBADF)) test_failed("allowed-bad-file-descriptor"); printf("Completed argument tests\n"); /* * Test operation of a single shot timer. */ ufd = make_new_timerfd(CLOCK_REALTIME, 0, 0, 500 * MILLISEC, 0, 0); if (ufd == -1) die("timerfd-MONOTONIC-ABSTIME"); if (read(ufd, &ticks, sizeof(ticks)) != sizeof(ticks)) die("read"); if (ticks != 1) test_failed("ticks-was-not-1") /* * The following timer should return an overrun count of 5 * for the read(). The fact that make multiple timerfd() * calls to retrieve the current timer settings should not * affect the overrun count. */ utmr.it_value.tv_sec = 1; utmr.it_value.tv_nsec = 0; utmr.it_interval.tv_sec = 2; utmr.it_interval.tv_nsec = 0; printf("\nMeasuring timer with interval %ld, frequency %ld, " "for 10 seconds\n", (long) utmr.it_value.tv_sec, (long) utmr.it_interval.tv_sec); ufd = timerfd(-1, CLOCK_REALTIME, 0, &utmr, NULL); if (ufd == -1) die("timerfd-MONOTONIC-ABSTIME"); for (j = 0; j < 10; j++) { sleep(1); ret = timerfd(ufd, -1, 0, NULL, &outmr); printf("%d - Got: %ld %9ld\n", j, (long) outmr.it_value.tv_sec, (long) outmr.it_value.tv_nsec); } if (read(ufd, &ticks, sizeof(ticks)) != sizeof(ticks)) die("read"); printf("Read (expiration count): %lld\n", ticks); if (ticks != 5) test_failed("ticks-was-not-5"); /* * Modify the settings of the previous timer. * The following timer should return an overrun count of 3 * for the read(). Making multiple timerfd() calls * to retrieve the current timer settings should not * affect the overrun count. */ utmr.it_value.tv_sec = 1; utmr.it_value.tv_nsec = 0; utmr.it_interval.tv_sec = 4; utmr.it_interval.tv_nsec = 0; printf("\nMeasuring timer with interval %ld, frequency %ld, " "for 10 seconds\n", (long) utmr.it_value.tv_sec, (long) utmr.it_interval.tv_sec); ret = timerfd(ufd, -1, 0, &utmr, &outmr); if (ret == -1) die("timerfd-MONOTONIC-ABSTIME"); for (j = 0; j < 10; j++) { sleep(1); ret = timerfd(ufd, -1, 0, NULL, &outmr); printf("%d - Got: %ld %9ld\n", j, (long) outmr.it_value.tv_sec, (long) outmr.it_value.tv_nsec); } if (read(ufd, &ticks, sizeof(ticks)) != sizeof(ticks)) die("read"); printf("Read (expiration count): %lld\n", ticks); if (ticks != 3) test_failed("ticks-was-not-3"); /* * If we set an interval timer to expire in the past, then it * the number of overruns should correspond to how far in the * past the timer was set. */ #define NEG_SECS 20 if (clock_gettime(CLOCK_REALTIME, &now) == -1) die("clock_gettime"); ufd = make_new_timerfd(CLOCK_REALTIME, TFD_TIMER_ABSTIME, now.tv_sec - NEG_SECS, now.tv_nsec, 1, 0); if (ufd == -1) die("timerfd-REALTIME-ABSTIME"); if (read(ufd, &ticks, sizeof(ticks)) != sizeof(ticks)) die("read"); printf("Read from already expired timer: %lld\n", ticks); if (ticks != (1 + NEG_SECS)) test_failed("wrong-value-read-from-past-timer"); if (failures == 0) printf("All tests passed successfully\n"); else printf("%d tests failed\n", failures); exit((failures == 0) ? EXIT_SUCCESS : EXIT_FAILURE); }