1/* 2 * Test program to verify that scheduled timers are executed in the 3 * correct order. 4 * 5 * Copyright (C) 2013 by Open Source Routing. 6 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") 7 * 8 * This file is part of Quagga 9 * 10 * Quagga is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2, or (at your option) any 13 * later version. 14 * 15 * Quagga is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Quagga; see the file COPYING. If not, write to the Free 22 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 23 * 02111-1307, USA. 24 */ 25 26#include <stdio.h> 27#include <unistd.h> 28 29#include <zebra.h> 30 31#include "memory.h" 32#include "pqueue.h" 33#include "prng.h" 34#include "thread.h" 35 36#define SCHEDULE_TIMERS 800 37#define REMOVE_TIMERS 200 38 39#define TIMESTR_LEN strlen("4294967296.999999") 40 41struct thread_master *master; 42 43static size_t log_buf_len; 44static size_t log_buf_pos; 45static char *log_buf; 46 47static size_t expected_buf_len; 48static size_t expected_buf_pos; 49static char *expected_buf; 50 51static struct prng *prng; 52 53static struct thread **timers; 54 55static int timers_pending; 56 57static void terminate_test(void) 58{ 59 int exit_code; 60 61 if (strcmp(log_buf, expected_buf)) 62 { 63 fprintf(stderr, "Expected output and received output differ.\n"); 64 fprintf(stderr, "---Expected output: ---\n%s", expected_buf); 65 fprintf(stderr, "---Actual output: ---\n%s", log_buf); 66 exit_code = 1; 67 } 68 else 69 { 70 printf("Expected output and actual output match.\n"); 71 exit_code = 0; 72 } 73 74 thread_master_free(master); 75 XFREE(MTYPE_TMP, log_buf); 76 XFREE(MTYPE_TMP, expected_buf); 77 prng_free(prng); 78 XFREE(MTYPE_TMP, timers); 79 80 exit(exit_code); 81} 82 83static int timer_func(struct thread *thread) 84{ 85 int rv; 86 87 rv = snprintf(log_buf + log_buf_pos, log_buf_len - log_buf_pos, 88 "%s\n", (char*)thread->arg); 89 assert(rv >= 0); 90 log_buf_pos += rv; 91 assert(log_buf_pos < log_buf_len); 92 XFREE(MTYPE_TMP, thread->arg); 93 94 timers_pending--; 95 if (!timers_pending) 96 terminate_test(); 97 98 return 0; 99} 100 101static int cmp_timeval(const void* a, const void *b) 102{ 103 const struct timeval *ta = *(struct timeval * const *)a; 104 const struct timeval *tb = *(struct timeval * const *)b; 105 106 if (timercmp(ta, tb, <)) 107 return -1; 108 if (timercmp(ta, tb, >)) 109 return 1; 110 return 0; 111} 112 113int main(int argc, char **argv) 114{ 115 int i, j; 116 struct thread t; 117 struct timeval **alarms; 118 119 master = thread_master_create(); 120 121 log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; 122 log_buf_pos = 0; 123 log_buf = XMALLOC(MTYPE_TMP, log_buf_len); 124 125 expected_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; 126 expected_buf_pos = 0; 127 expected_buf = XMALLOC(MTYPE_TMP, expected_buf_len); 128 129 prng = prng_new(0); 130 131 timers = XMALLOC(MTYPE_TMP, SCHEDULE_TIMERS * sizeof(*timers)); 132 133 for (i = 0; i < SCHEDULE_TIMERS; i++) 134 { 135 long interval_msec; 136 int ret; 137 char *arg; 138 139 /* Schedule timers to expire in 0..5 seconds */ 140 interval_msec = prng_rand(prng) % 5000; 141 arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1); 142 timers[i] = thread_add_timer_msec(master, timer_func, arg, interval_msec); 143 ret = snprintf(arg, TIMESTR_LEN + 1, "%ld.%06ld", 144 timers[i]->u.sands.tv_sec, timers[i]->u.sands.tv_usec); 145 assert(ret > 0); 146 assert((size_t)ret < TIMESTR_LEN + 1); 147 timers_pending++; 148 } 149 150 for (i = 0; i < REMOVE_TIMERS; i++) 151 { 152 int index; 153 154 index = prng_rand(prng) % SCHEDULE_TIMERS; 155 if (!timers[index]) 156 continue; 157 158 XFREE(MTYPE_TMP, timers[index]->arg); 159 thread_cancel(timers[index]); 160 timers[index] = NULL; 161 timers_pending--; 162 } 163 164 /* We create an array of pointers to the alarm times and sort 165 * that array. That sorted array is used to generate a string 166 * representing the expected "output" of the timers when they 167 * are run. */ 168 j = 0; 169 alarms = XMALLOC(MTYPE_TMP, timers_pending * sizeof(*alarms)); 170 for (i = 0; i < SCHEDULE_TIMERS; i++) 171 { 172 if (!timers[i]) 173 continue; 174 alarms[j++] = &timers[i]->u.sands; 175 } 176 qsort(alarms, j, sizeof(*alarms), cmp_timeval); 177 for (i = 0; i < j; i++) 178 { 179 int ret; 180 181 ret = snprintf(expected_buf + expected_buf_pos, 182 expected_buf_len - expected_buf_pos, 183 "%ld.%06ld\n", alarms[i]->tv_sec, alarms[i]->tv_usec); 184 assert(ret > 0); 185 expected_buf_pos += ret; 186 assert(expected_buf_pos < expected_buf_len); 187 } 188 XFREE(MTYPE_TMP, alarms); 189 190 while (thread_fetch(master, &t)) 191 thread_call(&t); 192 193 return 0; 194} 195