|  | /* | 
|  | *  timerfd() test by Davide Libenzi (test app for timerfd) | 
|  | *  Copyright (C) 2007  Davide Libenzi | 
|  | * | 
|  | *  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 | 
|  | * | 
|  | *  Davide Libenzi <davidel@xmailserver.org> | 
|  | * | 
|  | * | 
|  | *     $ gcc -o timerfd-test2 timerfd-test2.c -lrt | 
|  | * | 
|  | * NAME | 
|  | *	timerfd01.c | 
|  | * HISTORY | 
|  | *	28/05/2008 Initial contribution by Davide Libenzi <davidel@xmailserver.org> | 
|  | *	28/05/2008 Integrated to LTP by Subrata Modak <subrata@linux.vnet.ibm.com> | 
|  | *	2016-04-08 Deintegrated from LTP by Barret Rhoden <brho@cs.berkeley.edu> | 
|  | *		Ported to Akaros.  Added a few more tests. | 
|  | */ | 
|  |  | 
|  | #define _GNU_SOURCE | 
|  | #include <sys/syscall.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/signal.h> | 
|  | #include <sys/time.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <signal.h> | 
|  | #include <poll.h> | 
|  | #include <fcntl.h> | 
|  | #include <time.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <sys/timerfd.h> | 
|  | #include <sys/select.h> | 
|  | #include <parlib/alarm.h> | 
|  | #include <parlib/uthread.h> | 
|  |  | 
|  | struct tmr_type { | 
|  | int id; | 
|  | char const *name; | 
|  | }; | 
|  |  | 
|  | unsigned long long getustime(int clockid) | 
|  | { | 
|  | struct timespec tp; | 
|  |  | 
|  | if (clock_gettime((clockid_t) clockid, &tp)) { | 
|  | perror("clock_gettime"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return 1000000ULL * tp.tv_sec + tp.tv_nsec / 1000; | 
|  | } | 
|  |  | 
|  | void set_timespec(struct timespec *tmr, unsigned long long ustime) | 
|  | { | 
|  |  | 
|  | tmr->tv_sec = (time_t) (ustime / 1000000ULL); | 
|  | tmr->tv_nsec = (long)(1000ULL * (ustime % 1000000ULL)); | 
|  | } | 
|  |  | 
|  |  | 
|  | long waittmr(int tfd, int timeo) | 
|  | { | 
|  | u_int64_t ticks; | 
|  | fd_set rfds; | 
|  | struct timeval tv, *timeout = 0; | 
|  | int ret; | 
|  |  | 
|  | FD_ZERO(&rfds); | 
|  | FD_SET(tfd, &rfds); | 
|  | if (timeo != -1) { | 
|  | tv.tv_sec = timeo / 1000; | 
|  | tv.tv_usec = (timeo % 1000) * 1000; | 
|  | timeout = &tv; | 
|  | } | 
|  | ret = select(tfd + 1, &rfds, 0, 0, timeout); | 
|  | if (ret < 0) { | 
|  | perror("select"); | 
|  | return -1; | 
|  | } | 
|  | if (ret == 0) { | 
|  | fprintf(stdout, "no ticks happened\n"); | 
|  | return -1; | 
|  | } | 
|  | if (read(tfd, &ticks, sizeof(ticks)) != sizeof(ticks)) { | 
|  | perror("timerfd read"); | 
|  | return -1; | 
|  | } | 
|  | return ticks; | 
|  | } | 
|  |  | 
|  | int main(int ac, char **av) | 
|  | { | 
|  | int i, tfd; | 
|  | long ticks; | 
|  | unsigned long long tnow, ttmr; | 
|  | u_int64_t uticks; | 
|  | struct itimerspec tmr; | 
|  | struct tmr_type clks[] = { | 
|  | {CLOCK_MONOTONIC, "CLOCK MONOTONIC"}, | 
|  | {CLOCK_REALTIME, "CLOCK REALTIME"}, | 
|  | }; | 
|  |  | 
|  | for (i = 0; i < sizeof(clks) / sizeof(clks[0]); i++) { | 
|  | fprintf(stdout, | 
|  | "\n\n---------------------------------------\n"); | 
|  | fprintf(stdout, "| testing %s\n", clks[i].name); | 
|  | fprintf(stdout, "---------------------------------------\n\n"); | 
|  |  | 
|  | fprintf(stdout, "relative timer test (at 500 ms) ...\n"); | 
|  | set_timespec(&tmr.it_value, 500 * 1000); | 
|  | set_timespec(&tmr.it_interval, 0); | 
|  | tnow = getustime(clks[i].id); | 
|  | if ((tfd = timerfd_create(clks[i].id, 0)) == -1) { | 
|  | perror("timerfd"); | 
|  | return 1; | 
|  | } | 
|  | fprintf(stdout, "timerfd = %d\n", tfd); | 
|  |  | 
|  | if (timerfd_settime(tfd, 0, &tmr, NULL)) { | 
|  | perror("timerfd_settime"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | fprintf(stdout, "waiting timer ...\n"); | 
|  | ticks = waittmr(tfd, -1); | 
|  | ttmr = getustime(clks[i].id); | 
|  | if (ticks <= 0) | 
|  | fprintf(stdout, "whooops! no timer showed up!\n"); | 
|  | else | 
|  | fprintf(stdout, "got timer ticks (%ld) after %llu ms\n", | 
|  | ticks, (ttmr - tnow) / 1000); | 
|  |  | 
|  | fprintf(stdout, "absolute timer test (at 500 ms) ...\n"); | 
|  | tnow = getustime(clks[i].id); | 
|  | set_timespec(&tmr.it_value, tnow + 500 * 1000); | 
|  | set_timespec(&tmr.it_interval, 0); | 
|  | if (timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tmr, NULL)) { | 
|  | perror("timerfd_settime"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | fprintf(stdout, "waiting timer ...\n"); | 
|  | ticks = waittmr(tfd, -1); | 
|  | ttmr = getustime(clks[i].id); | 
|  | if (ticks <= 0) | 
|  | fprintf(stdout, "whooops! no timer showed up!\n"); | 
|  | else | 
|  | fprintf(stdout, "got timer ticks (%ld) after %llu ms\n", | 
|  | ticks, (ttmr - tnow) / 1000); | 
|  |  | 
|  | fprintf(stdout, "sequential timer test (100 ms clock) ...\n"); | 
|  | tnow = getustime(clks[i].id); | 
|  | set_timespec(&tmr.it_value, tnow + 100 * 1000); | 
|  | set_timespec(&tmr.it_interval, 100 * 1000); | 
|  | if (timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tmr, NULL)) { | 
|  | perror("timerfd_settime"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | fprintf(stdout, "sleeping 1 second ...\n"); | 
|  | sleep(1); | 
|  | if (timerfd_gettime(tfd, &tmr)) { | 
|  | perror("timerfd_gettime"); | 
|  | return 1; | 
|  | } | 
|  | fprintf(stdout, "timerfd_gettime returned:\n" | 
|  | "\tit_value = { %ld, %ld } it_interval = { %ld, %ld }\n", | 
|  | (long)tmr.it_value.tv_sec, (long)tmr.it_value.tv_nsec, | 
|  | (long)tmr.it_interval.tv_sec, | 
|  | (long)tmr.it_interval.tv_nsec); | 
|  | fprintf(stdout, "sleeping 1 second ...\n"); | 
|  | sleep(1); | 
|  |  | 
|  | fprintf(stdout, "waiting timer ...\n"); | 
|  | ticks = waittmr(tfd, -1); | 
|  | ttmr = getustime(clks[i].id); | 
|  | if (ticks <= 0) | 
|  | fprintf(stdout, "whooops! no timer showed up!\n"); | 
|  | else | 
|  | fprintf(stdout, "got timer ticks (%ld) after %llu ms\n", | 
|  | ticks, (ttmr - tnow) / 1000); | 
|  |  | 
|  | fprintf(stdout, "O_NONBLOCK test ...\n"); | 
|  | tnow = getustime(clks[i].id); | 
|  | set_timespec(&tmr.it_value, 100 * 1000); | 
|  | set_timespec(&tmr.it_interval, 0); | 
|  | if (timerfd_settime(tfd, 0, &tmr, NULL)) { | 
|  | perror("timerfd_settime"); | 
|  | return 1; | 
|  | } | 
|  | fprintf(stdout, "timerfd = %d\n", tfd); | 
|  |  | 
|  | fprintf(stdout, "waiting timer (flush the single tick) ...\n"); | 
|  | ticks = waittmr(tfd, -1); | 
|  | ttmr = getustime(clks[i].id); | 
|  | if (ticks <= 0) | 
|  | fprintf(stdout, "whooops! no timer showed up!\n"); | 
|  | else | 
|  | fprintf(stdout, "got timer ticks (%ld) after %llu ms\n", | 
|  | ticks, (ttmr - tnow) / 1000); | 
|  |  | 
|  | fcntl(tfd, F_SETFL, fcntl(tfd, F_GETFL, 0) | O_NONBLOCK); | 
|  |  | 
|  | if (read(tfd, &uticks, sizeof(uticks)) > 0) | 
|  | fprintf(stdout, | 
|  | "whooops! timer ticks not zero when should have been\n"); | 
|  | else if (errno != EAGAIN) | 
|  | fprintf(stdout, | 
|  | "whooops! bad errno value (%d = '%s')!\n", | 
|  | errno, strerror(errno)); | 
|  | else | 
|  | fprintf(stdout, "success\n"); | 
|  |  | 
|  | /* try a select loop with O_NONBLOCK */ | 
|  | fd_set rfds; | 
|  | bool has_selected = FALSE; | 
|  | int ret; | 
|  |  | 
|  | FD_ZERO(&rfds); | 
|  | FD_SET(tfd, &rfds); | 
|  | set_timespec(&tmr.it_value, 1000000); | 
|  | set_timespec(&tmr.it_interval, 0); | 
|  | if (timerfd_settime(tfd, 0, &tmr, NULL)) { | 
|  | perror("timerfd_settime"); | 
|  | exit(-1); | 
|  | } | 
|  | while (1) { | 
|  | ret = read(tfd, &uticks, sizeof(uticks)); | 
|  | if (ret < 0) { | 
|  | if (errno != EAGAIN) { | 
|  | perror("select read"); | 
|  | exit(-1); | 
|  | } | 
|  | } else { | 
|  | if (ret != sizeof(uticks)) { | 
|  | fprintf(stdout, "short read! (bad)\n"); | 
|  | exit(-1); | 
|  | } | 
|  | if (uticks) | 
|  | break; | 
|  | } | 
|  | if (select(tfd + 1, &rfds, 0, 0, 0) < 0) { | 
|  | perror("select"); | 
|  | return -1; | 
|  | } | 
|  | has_selected = TRUE; | 
|  | } | 
|  | if (!has_selected) { | 
|  | fprintf(stdout, "Failed to try to select!\n"); | 
|  | exit(-1); | 
|  | } | 
|  | fprintf(stdout, "more success\n"); | 
|  | fcntl(tfd, F_SETFL, fcntl(tfd, F_GETFL, 0) & ~O_NONBLOCK); | 
|  |  | 
|  | /* let's make sure it actually blocks too. */ | 
|  | struct alarm_waiter waiter; | 
|  |  | 
|  | init_awaiter(&waiter, alarm_abort_sysc); | 
|  | waiter.data = current_uthread; | 
|  | set_awaiter_rel(&waiter, 1000000); | 
|  |  | 
|  | set_timespec(&tmr.it_value, 10000000); | 
|  | set_timespec(&tmr.it_interval, 0); | 
|  | if (timerfd_settime(tfd, 0, &tmr, NULL)) { | 
|  | perror("timerfd_settime"); | 
|  | exit(-1); | 
|  | } | 
|  | set_alarm(&waiter); | 
|  | ret = read(tfd, &uticks, sizeof(uticks)); | 
|  | unset_alarm(&waiter); | 
|  | if (ret > 0) { | 
|  | fprintf(stdout, | 
|  | "Failed to block when we should have!\n"); | 
|  | exit(-1); | 
|  | } | 
|  | fprintf(stdout, "done (still success)\n"); | 
|  |  | 
|  | close(tfd); | 
|  | } | 
|  | } |