/* * (C) 2008 by Holger Hans Peter Freyther * (C) 2011 by Harald Welte * All Rights Reserved * * Authors: Holger Hans Peter Freyther * Pablo Neira Ayuso * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include "../config.h" static void main_timer_fired(void *data); static void secondary_timer_fired(void *data); static unsigned int main_timer_step = 0; static struct osmo_timer_list main_timer = { .cb = main_timer_fired, .data = &main_timer_step, }; static LLIST_HEAD(timer_test_list); struct test_timer { struct llist_head head; struct osmo_timer_list timer; struct timeval start; struct timeval stop; }; /* number of test steps. We add fact(steps) timers in the whole test. */ #define MAIN_TIMER_NSTEPS 16 /* time between two steps, in secs. */ #define TIME_BETWEEN_STEPS 1 /* timer imprecision that we accept for this test: 10 milliseconds. */ #define TIMER_PRES_SECS 0 #define TIMER_PRES_USECS 20000 static int timer_nsteps = MAIN_TIMER_NSTEPS; static unsigned int expired_timers = 0; static unsigned int total_timers = 0; static unsigned int too_late = 0; static void main_timer_fired(void *data) { unsigned int *step = data; unsigned int add_in_this_step; int i; if (*step == timer_nsteps) { fprintf(stderr, "Main timer has finished, please, " "wait a bit for the final report.\n"); return; } /* add 2^step pair of timers per step. */ add_in_this_step = (1 << *step); for (i=0; istart, NULL); v->timer.cb = secondary_timer_fired; v->timer.data = v; unsigned int seconds = (random() % 10) + 1; v->stop.tv_sec = v->start.tv_sec + seconds; osmo_timer_schedule(&v->timer, seconds, 0); llist_add(&v->head, &timer_test_list); } fprintf(stderr, "added %d timers in step %u (expired=%u)\n", add_in_this_step, *step, expired_timers); total_timers += add_in_this_step; osmo_timer_schedule(&main_timer, TIME_BETWEEN_STEPS, 0); (*step)++; } static void secondary_timer_fired(void *data) { struct test_timer *v = data, *this, *tmp; struct timeval current, res, precision = { 1, 0 }; gettimeofday(¤t, NULL); timersub(¤t, &v->stop, &res); if (timercmp(&res, &precision, >)) { fprintf(stderr, "ERROR: timer %p has expired too late!\n", &v->timer); too_late++; } llist_del(&v->head); talloc_free(data); expired_timers++; if (expired_timers == total_timers) { fprintf(stdout, "test over: added=%u expired=%u too_late=%u \n", total_timers, expired_timers, too_late); exit(EXIT_SUCCESS); } /* randomly (10%) deletion of timers. */ llist_for_each_entry_safe(this, tmp, &timer_test_list, head) { if ((random() % 100) < 10) { osmo_timer_del(&this->timer); llist_del(&this->head); talloc_free(this); expired_timers++; } } } static void alarm_handler(int signum) { fprintf(stderr, "ERROR: We took too long to run the timer test, " "something seems broken, aborting.\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int c; if (signal(SIGALRM, alarm_handler) == SIG_ERR) { perror("cannot register signal handler"); exit(EXIT_FAILURE); } while ((c = getopt_long(argc, argv, "s:", NULL, NULL)) != -1) { switch(c) { case 's': timer_nsteps = atoi(optarg); if (timer_nsteps <= 0) { fprintf(stderr, "%s: steps must be > 0\n", argv[0]); exit(EXIT_FAILURE); } break; default: exit(EXIT_FAILURE); } } fprintf(stdout, "Running timer test for %u steps, accepting " "imprecision of %u.%.6u seconds\n", timer_nsteps, TIMER_PRES_SECS, TIMER_PRES_USECS); osmo_timer_schedule(&main_timer, 1, 0); /* if the test takes too long, we may consider that the timer scheduler * has hung. We set some maximum wait time which is the double of the * maximum timeout randomly set (10 seconds, worst case) plus the * number of steps (since some of them are reset each step). */ alarm(2 * (10 + timer_nsteps)); #ifdef HAVE_SYS_SELECT_H while (1) { osmo_select_main(0); } #else fprintf(stdout, "Select not supported on this platform!\n"); #endif }