| 1 | /*
|
|---|
| 2 | * Copyright 2004, Broadcom Corporation
|
|---|
| 3 | * All Rights Reserved.
|
|---|
| 4 | *
|
|---|
| 5 | * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
|
|---|
| 6 | * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
|
|---|
| 7 | * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
|---|
| 8 | * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
|
|---|
| 9 | *
|
|---|
| 10 | * Low resolution timer interface linux specific implementation.
|
|---|
| 11 | *
|
|---|
| 12 | * $Id: linux_timer.c 1540 2005-07-24 19:58:14Z nbd $
|
|---|
| 13 | */
|
|---|
| 14 |
|
|---|
| 15 | /*
|
|---|
| 16 | * debug facilities
|
|---|
| 17 | */
|
|---|
| 18 | #define TIMER_DEBUG 0
|
|---|
| 19 | #if TIMER_DEBUG
|
|---|
| 20 | #define TIMERDBG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
|
|---|
| 21 | #else
|
|---|
| 22 | #define TIMERDBG(fmt, args...)
|
|---|
| 23 | #endif
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 | /*
|
|---|
| 27 | * POSIX timer support for Linux. Taken from linux_timer.c in upnp
|
|---|
| 28 | */
|
|---|
| 29 |
|
|---|
| 30 | #define __USE_GNU
|
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 | #include <stdlib.h> // for malloc, free, etc.
|
|---|
| 34 | #include <string.h> // for memset, strncasecmp, etc.
|
|---|
| 35 | #include <assert.h> // for assert, of course.
|
|---|
| 36 | #include <signal.h> // for sigemptyset, etc.
|
|---|
| 37 | #include <stdio.h> // for printf, etc.
|
|---|
| 38 | #include <sys/time.h>
|
|---|
| 39 | #include <time.h>
|
|---|
| 40 |
|
|---|
| 41 | /* define TIMER_PROFILE to enable code which guages how accurate the timer functions are.
|
|---|
| 42 | For each expiring timer the code will print the expected time interval and the actual time interval.
|
|---|
| 43 | #define TIMER_PROFILE
|
|---|
| 44 | */
|
|---|
| 45 | #undef TIMER_PROFILE
|
|---|
| 46 |
|
|---|
| 47 | /*
|
|---|
| 48 | timer_cancel( ) - cancel a timer
|
|---|
| 49 | timer_connect( ) - connect a user routine to the timer signal
|
|---|
| 50 | timer_create( ) - allocate a timer using the specified clock for a timing base (POSIX)
|
|---|
| 51 | timer_delete( ) - remove a previously created timer (POSIX)
|
|---|
| 52 | timer_gettime( ) - get the remaining time before expiration and the reload value (POSIX)
|
|---|
| 53 | timer_getoverrun( ) - return the timer expiration overrun (POSIX)
|
|---|
| 54 | timer_settime( ) - set the time until the next expiration and arm timer (POSIX)
|
|---|
| 55 | nanosleep( ) - suspend the current task until the time interval elapses (POSIX)
|
|---|
| 56 | */
|
|---|
| 57 |
|
|---|
| 58 | #define MS_PER_SEC 1000
|
|---|
| 59 | #define US_PER_SEC 1000000
|
|---|
| 60 | #define US_PER_MS 1000
|
|---|
| 61 | #define UCLOCKS_PER_SEC 1000000
|
|---|
| 62 |
|
|---|
| 63 | typedef void (*event_callback_t)(timer_t, int);
|
|---|
| 64 |
|
|---|
| 65 | #ifndef TIMESPEC_TO_TIMEVAL
|
|---|
| 66 | # define TIMESPEC_TO_TIMEVAL(tv, ts) { \
|
|---|
| 67 | (tv)->tv_sec = (ts)->tv_sec; \
|
|---|
| 68 | (tv)->tv_usec = (ts)->tv_nsec / 1000; \
|
|---|
| 69 | }
|
|---|
| 70 | #endif
|
|---|
| 71 |
|
|---|
| 72 | #ifndef TIMEVAL_TO_TIMESPEC
|
|---|
| 73 | # define TIMEVAL_TO_TIMESPEC(tv, ts) { \
|
|---|
| 74 | (ts)->tv_sec = (tv)->tv_sec; \
|
|---|
| 75 | (ts)->tv_nsec = (tv)->tv_usec * 1000; \
|
|---|
| 76 | }
|
|---|
| 77 | #endif
|
|---|
| 78 |
|
|---|
| 79 | #define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
|
|---|
| 80 |
|
|---|
| 81 | #define timerroundup(t,g) \
|
|---|
| 82 | do { \
|
|---|
| 83 | if (!timerisset(t)) (t)->tv_usec=1; \
|
|---|
| 84 | if ((t)->tv_sec == 0) (t)->tv_usec=ROUNDUP((t)->tv_usec, g); \
|
|---|
| 85 | } while (0)
|
|---|
| 86 |
|
|---|
| 87 | typedef long uclock_t;
|
|---|
| 88 |
|
|---|
| 89 | #define TFLAG_NONE 0
|
|---|
| 90 | #define TFLAG_CANCELLED (1<<0)
|
|---|
| 91 | #define TFLAG_DELETED (1<<1)
|
|---|
| 92 |
|
|---|
| 93 | struct event {
|
|---|
| 94 | struct timeval it_interval;
|
|---|
| 95 | struct timeval it_value;
|
|---|
| 96 | event_callback_t func;
|
|---|
| 97 | int arg;
|
|---|
| 98 | unsigned short flags;
|
|---|
| 99 | struct event *next;
|
|---|
| 100 | #ifdef TIMER_PROFILE
|
|---|
| 101 | uint expected_ms;
|
|---|
| 102 | uclock_t start;
|
|---|
| 103 | #endif
|
|---|
| 104 | };
|
|---|
| 105 |
|
|---|
| 106 | void timer_cancel(timer_t timerid);
|
|---|
| 107 |
|
|---|
| 108 | static void alarm_handler(int i);
|
|---|
| 109 | static void check_event_queue();
|
|---|
| 110 | static void print_event_queue();
|
|---|
| 111 | static void check_timer();
|
|---|
| 112 | #if THIS_FINDS_USE
|
|---|
| 113 | static int count_queue(struct event *);
|
|---|
| 114 | #endif
|
|---|
| 115 | static int timer_change_settime(timer_t timer_id, const struct itimerspec *timer_spec);
|
|---|
| 116 | void block_timer();
|
|---|
| 117 | void unblock_timer();
|
|---|
| 118 |
|
|---|
| 119 | static struct event *event_queue = NULL;
|
|---|
| 120 | static struct event *event_freelist;
|
|---|
| 121 | static uint g_granularity;
|
|---|
| 122 | static int g_maxevents = 0;
|
|---|
| 123 |
|
|---|
| 124 | uclock_t uclock()
|
|---|
| 125 | {
|
|---|
| 126 | struct timeval tv;
|
|---|
| 127 |
|
|---|
| 128 | gettimeofday(&tv, NULL);
|
|---|
| 129 | return ((tv.tv_sec * US_PER_SEC) + tv.tv_usec);
|
|---|
| 130 | }
|
|---|
| 131 |
|
|---|
| 132 |
|
|---|
| 133 | void init_event_queue(int n)
|
|---|
| 134 | {
|
|---|
| 135 | int i;
|
|---|
| 136 | struct itimerval tv;
|
|---|
| 137 |
|
|---|
| 138 | g_maxevents = n;
|
|---|
| 139 | event_freelist = (struct event *) malloc(n * sizeof(struct event));
|
|---|
| 140 | memset(event_freelist, 0, n * sizeof(struct event));
|
|---|
| 141 |
|
|---|
| 142 | for (i = 0; i < (n-1); i++)
|
|---|
| 143 | event_freelist[i].next = &event_freelist[i+1];
|
|---|
| 144 |
|
|---|
| 145 | event_freelist[i].next = NULL;
|
|---|
| 146 |
|
|---|
| 147 | tv.it_interval.tv_sec = 0;
|
|---|
| 148 | tv.it_interval.tv_usec = 1;
|
|---|
| 149 | tv.it_value.tv_sec = 0;
|
|---|
| 150 | tv.it_value.tv_usec = 0;
|
|---|
| 151 | setitimer (ITIMER_REAL, &tv, 0);
|
|---|
| 152 | setitimer (ITIMER_REAL, 0, &tv);
|
|---|
| 153 | g_granularity = tv.it_interval.tv_usec;
|
|---|
| 154 |
|
|---|
| 155 | signal(SIGALRM, alarm_handler);
|
|---|
| 156 | }
|
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 | int clock_gettime(
|
|---|
| 160 | clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */
|
|---|
| 161 | struct timespec * tp /* where to store current time */
|
|---|
| 162 | )
|
|---|
| 163 | {
|
|---|
| 164 | struct timeval tv;
|
|---|
| 165 | int n;
|
|---|
| 166 |
|
|---|
| 167 |
|
|---|
| 168 | n = gettimeofday(&tv, NULL);
|
|---|
| 169 | TIMEVAL_TO_TIMESPEC(&tv, tp);
|
|---|
| 170 |
|
|---|
| 171 | return n;
|
|---|
| 172 | }
|
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 | int timer_create(
|
|---|
| 176 | clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */
|
|---|
| 177 | struct sigevent * evp, /* user event handler */
|
|---|
| 178 | timer_t * pTimer /* ptr to return value */
|
|---|
| 179 | )
|
|---|
| 180 | {
|
|---|
| 181 | struct event *event;
|
|---|
| 182 |
|
|---|
| 183 | if (clock_id != CLOCK_REALTIME) {
|
|---|
| 184 | TIMERDBG("timer_create can only support clock id CLOCK_REALTIME");
|
|---|
| 185 | exit(1);
|
|---|
| 186 | }
|
|---|
| 187 |
|
|---|
| 188 | if (evp != NULL) {
|
|---|
| 189 | if (evp->sigev_notify != SIGEV_SIGNAL || evp->sigev_signo != SIGALRM) {
|
|---|
| 190 | TIMERDBG("timer_create can only support signalled alarms using SIGALRM");
|
|---|
| 191 | exit(1);
|
|---|
| 192 | }
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | event = event_freelist;
|
|---|
| 196 | if (event == NULL) {
|
|---|
| 197 | print_event_queue();
|
|---|
| 198 | }
|
|---|
| 199 | assert(event != NULL);
|
|---|
| 200 |
|
|---|
| 201 | event->flags = TFLAG_NONE;
|
|---|
| 202 |
|
|---|
| 203 | event_freelist = event->next;
|
|---|
| 204 | event->next = NULL;
|
|---|
| 205 |
|
|---|
| 206 | check_event_queue();
|
|---|
| 207 |
|
|---|
| 208 | *pTimer = (timer_t) event;
|
|---|
| 209 |
|
|---|
| 210 | return 0;
|
|---|
| 211 | }
|
|---|
| 212 |
|
|---|
| 213 | int timer_delete(
|
|---|
| 214 | timer_t timerid /* timer ID */
|
|---|
| 215 | )
|
|---|
| 216 | {
|
|---|
| 217 | struct event *event = (struct event *) timerid;
|
|---|
| 218 |
|
|---|
| 219 | if (event->flags & TFLAG_DELETED) {
|
|---|
| 220 | TIMERDBG("Cannot delete a deleted event");
|
|---|
| 221 | return 1;
|
|---|
| 222 | }
|
|---|
| 223 |
|
|---|
| 224 | timer_cancel(timerid);
|
|---|
| 225 |
|
|---|
| 226 | event->flags |= TFLAG_DELETED;
|
|---|
| 227 |
|
|---|
| 228 | event->next = event_freelist;
|
|---|
| 229 | event_freelist = event;
|
|---|
| 230 |
|
|---|
| 231 | return 0;
|
|---|
| 232 | }
|
|---|
| 233 |
|
|---|
| 234 | int timer_connect
|
|---|
| 235 | (
|
|---|
| 236 | timer_t timerid, /* timer ID */
|
|---|
| 237 | void (*routine)(timer_t, int), /* user routine */
|
|---|
| 238 | int arg /* user argument */
|
|---|
| 239 | )
|
|---|
| 240 | {
|
|---|
| 241 | struct event *event = (struct event *) timerid;
|
|---|
| 242 |
|
|---|
| 243 | assert(routine != NULL);
|
|---|
| 244 | event->func = routine;
|
|---|
| 245 | event->arg = arg;
|
|---|
| 246 |
|
|---|
| 247 | return 0;
|
|---|
| 248 | }
|
|---|
| 249 |
|
|---|
| 250 | /*
|
|---|
| 251 | * Please Call this function only from the call back functions of the alarm_handler.
|
|---|
| 252 | * This is just a hack
|
|---|
| 253 | */
|
|---|
| 254 | int timer_change_settime
|
|---|
| 255 | (
|
|---|
| 256 | timer_t timerid, /* timer ID */
|
|---|
| 257 | const struct itimerspec * value /* time to be set */
|
|---|
| 258 | )
|
|---|
| 259 | {
|
|---|
| 260 | struct event *event = (struct event *) timerid;
|
|---|
| 261 |
|
|---|
| 262 | TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
|
|---|
| 263 | TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
|
|---|
| 264 |
|
|---|
| 265 | return 1;
|
|---|
| 266 | }
|
|---|
| 267 |
|
|---|
| 268 | int timer_settime
|
|---|
| 269 | (
|
|---|
| 270 | timer_t timerid, /* timer ID */
|
|---|
| 271 | int flags, /* absolute or relative */
|
|---|
| 272 | const struct itimerspec * value, /* time to be set */
|
|---|
| 273 | struct itimerspec * ovalue /* previous time set (NULL=no result) */
|
|---|
| 274 | )
|
|---|
| 275 | {
|
|---|
| 276 | struct itimerval itimer;
|
|---|
| 277 | struct event *event = (struct event *) timerid;
|
|---|
| 278 | struct event **ppevent;
|
|---|
| 279 |
|
|---|
| 280 | TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
|
|---|
| 281 | TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
|
|---|
| 282 |
|
|---|
| 283 | /* if .it_value is zero, the timer is disarmed */
|
|---|
| 284 | if (!timerisset(&event->it_value)) {
|
|---|
| 285 | timer_cancel(timerid);
|
|---|
| 286 | return 0;
|
|---|
| 287 | }
|
|---|
| 288 |
|
|---|
| 289 | block_timer();
|
|---|
| 290 |
|
|---|
| 291 | #ifdef TIMER_PROFILE
|
|---|
| 292 | event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / US_PER_MS);
|
|---|
| 293 | event->start = uclock();
|
|---|
| 294 | #endif
|
|---|
| 295 | if (event->next) {
|
|---|
| 296 | TIMERDBG("calling timer_settime with a timer that is already on the queue.");
|
|---|
| 297 | }
|
|---|
| 298 |
|
|---|
| 299 |
|
|---|
| 300 | /* We always want to make sure that the event at the head of the
|
|---|
| 301 | queue has a timeout greater than the itimer granularity.
|
|---|
| 302 | Otherwise we end up with the situation that the time remaining
|
|---|
| 303 | on an itimer is greater than the time at the head of the queue
|
|---|
| 304 | in the first place. */
|
|---|
| 305 | timerroundup(&event->it_value, g_granularity);
|
|---|
| 306 |
|
|---|
| 307 | timerclear(&itimer.it_value);
|
|---|
| 308 | getitimer(ITIMER_REAL, &itimer);
|
|---|
| 309 | if (timerisset(&itimer.it_value)) {
|
|---|
| 310 | // reset the top timer to have an interval equal to the remaining interval
|
|---|
| 311 | // when the timer was cancelled.
|
|---|
| 312 | if (event_queue) {
|
|---|
| 313 | if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
|
|---|
| 314 | // it is an error if the amount of time remaining is more than the amount of time
|
|---|
| 315 | // requested by the top event.
|
|---|
| 316 | //
|
|---|
| 317 | TIMERDBG("timer_settime: TIMER ERROR!");
|
|---|
| 318 |
|
|---|
| 319 | } else {
|
|---|
| 320 | // some portion of the top event has already expired.
|
|---|
| 321 | // Reset the interval of the top event to remaining
|
|---|
| 322 | // time left in that interval.
|
|---|
| 323 | //
|
|---|
| 324 | event_queue->it_value = itimer.it_value;
|
|---|
| 325 |
|
|---|
| 326 | // if we were the earliest timer before now, we are still the earliest timer now.
|
|---|
| 327 | // we do not need to reorder the list.
|
|---|
| 328 | }
|
|---|
| 329 | }
|
|---|
| 330 | }
|
|---|
| 331 |
|
|---|
| 332 | // Now, march down the list, decrementing the new timer by the
|
|---|
| 333 | // current it_value of each event on the queue.
|
|---|
| 334 | ppevent = &event_queue;
|
|---|
| 335 | while (*ppevent) {
|
|---|
| 336 | if ( timercmp(&(event->it_value), &((*ppevent)->it_value), <) ) {
|
|---|
| 337 | // if the proposed event will trigger sooner than the next event
|
|---|
| 338 | // in the queue, we will insert the new event just before the next one.
|
|---|
| 339 | //
|
|---|
| 340 | // we also need to adjust the delta value to the next event.
|
|---|
| 341 | timersub(&((*ppevent)->it_value), &(event->it_value), &((*ppevent)->it_value));
|
|---|
| 342 | break;
|
|---|
| 343 | }
|
|---|
| 344 | // subtract the interval of the next event from the proposed interval.
|
|---|
| 345 | timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value));
|
|---|
| 346 |
|
|---|
| 347 | ppevent = &((*ppevent)->next);
|
|---|
| 348 | }
|
|---|
| 349 |
|
|---|
| 350 | // we have found our proper place in the queue,
|
|---|
| 351 | // link our new event into the pending event queue.
|
|---|
| 352 | event->next = *ppevent;
|
|---|
| 353 | *ppevent = event;
|
|---|
| 354 |
|
|---|
| 355 | check_event_queue();
|
|---|
| 356 |
|
|---|
| 357 | // if our new event ended up at the front of the queue, reissue the timer.
|
|---|
| 358 | if (event == event_queue) {
|
|---|
| 359 | timerroundup(&event_queue->it_value, g_granularity);
|
|---|
| 360 | timerclear(&itimer.it_interval);
|
|---|
| 361 | itimer.it_value = event_queue->it_value;
|
|---|
| 362 |
|
|---|
| 363 | // we want to be sure to never turn off the timer completely,
|
|---|
| 364 | // so if the next interval is zero, set it to some small value.
|
|---|
| 365 | if (!timerisset(&(itimer.it_value)))
|
|---|
| 366 | itimer.it_value = (struct timeval) { 0, 1 };
|
|---|
| 367 |
|
|---|
| 368 | assert(!timerisset(&itimer.it_interval));
|
|---|
| 369 | assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity);
|
|---|
| 370 | assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= g_granularity);
|
|---|
| 371 | setitimer(ITIMER_REAL, &itimer, NULL);
|
|---|
| 372 | check_timer();
|
|---|
| 373 | }
|
|---|
| 374 |
|
|---|
| 375 | event->flags &= ~TFLAG_CANCELLED;
|
|---|
| 376 |
|
|---|
| 377 | unblock_timer();
|
|---|
| 378 |
|
|---|
| 379 | return 0;
|
|---|
| 380 | }
|
|---|
| 381 |
|
|---|
| 382 | static void check_timer()
|
|---|
| 383 | {
|
|---|
| 384 | struct itimerval itimer;
|
|---|
| 385 |
|
|---|
| 386 | getitimer(ITIMER_REAL, &itimer);
|
|---|
| 387 | if (timerisset(&itimer.it_interval)) {
|
|---|
| 388 | TIMERDBG("ERROR timer interval is set.");
|
|---|
| 389 | }
|
|---|
| 390 | if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
|
|---|
| 391 | TIMERDBG("ERROR timer expires later than top event.");
|
|---|
| 392 | }
|
|---|
| 393 | }
|
|---|
| 394 |
|
|---|
| 395 |
|
|---|
| 396 | static void check_event_queue()
|
|---|
| 397 | {
|
|---|
| 398 | struct timeval sum;
|
|---|
| 399 | struct event *event;
|
|---|
| 400 | int i = 0;
|
|---|
| 401 |
|
|---|
| 402 | #ifdef notdef
|
|---|
| 403 | int nfree = 0;
|
|---|
| 404 | struct event *p;
|
|---|
| 405 | for (p = event_freelist; p; p = p->next)
|
|---|
| 406 | nfree++;
|
|---|
| 407 | printf("%d free events\n", nfree);
|
|---|
| 408 | #endif
|
|---|
| 409 |
|
|---|
| 410 | timerclear(&sum);
|
|---|
| 411 | for (event = event_queue; event; event = event->next) {
|
|---|
| 412 | if (i > g_maxevents) {
|
|---|
| 413 | TIMERDBG("timer queue looks like it loops back on itself!");
|
|---|
| 414 | print_event_queue();
|
|---|
| 415 | exit(1);
|
|---|
| 416 | }
|
|---|
| 417 | i++;
|
|---|
| 418 | }
|
|---|
| 419 | }
|
|---|
| 420 |
|
|---|
| 421 | #if THIS_FINDS_USE
|
|---|
| 422 | /* The original upnp version has this unused function, so I left it in
|
|---|
| 423 | to maintain the resemblance. */
|
|---|
| 424 | static int count_queue(struct event *event_queue)
|
|---|
| 425 | {
|
|---|
| 426 | struct event *event;
|
|---|
| 427 | int i = 0;
|
|---|
| 428 | for (event = event_queue; event; event = event->next)
|
|---|
| 429 | i++;
|
|---|
| 430 | return i;
|
|---|
| 431 | }
|
|---|
| 432 | #endif
|
|---|
| 433 |
|
|---|
| 434 | static void print_event_queue()
|
|---|
| 435 | {
|
|---|
| 436 | struct event *event;
|
|---|
| 437 | int i = 0;
|
|---|
| 438 |
|
|---|
| 439 | for (event = event_queue; event; event = event->next) {
|
|---|
| 440 | printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n",
|
|---|
| 441 | i++, (unsigned int) event, (unsigned int) event->next, (int) event->it_value.tv_sec, (int) event->it_value.tv_usec, event->func);
|
|---|
| 442 | if (i > g_maxevents) {
|
|---|
| 443 | printf("...(giving up)\n");
|
|---|
| 444 | break;
|
|---|
| 445 | }
|
|---|
| 446 | }
|
|---|
| 447 | }
|
|---|
| 448 |
|
|---|
| 449 | // The top element of the event queue must have expired.
|
|---|
| 450 | // Remove that element, run its function, and reset the timer.
|
|---|
| 451 | // if there is no interval, recycle the event structure.
|
|---|
| 452 | static void alarm_handler(int i)
|
|---|
| 453 | {
|
|---|
| 454 | struct event *event, **ppevent;
|
|---|
| 455 | struct itimerval itimer;
|
|---|
| 456 | struct timeval small_interval = { 0, g_granularity/2 };
|
|---|
| 457 | #ifdef TIMER_PROFILE
|
|---|
| 458 | uint junk;
|
|---|
| 459 | uclock_t end;
|
|---|
| 460 | uint actual;
|
|---|
| 461 | #endif
|
|---|
| 462 |
|
|---|
| 463 | block_timer();
|
|---|
| 464 |
|
|---|
| 465 | // Loop through the event queue and remove the first event plus any
|
|---|
| 466 | // subsequent events that will expire very soon thereafter (within 'small_interval'}.
|
|---|
| 467 | //
|
|---|
| 468 | do {
|
|---|
| 469 | // remove the top event.
|
|---|
| 470 | event = event_queue;
|
|---|
| 471 | event_queue = event_queue->next;
|
|---|
| 472 | event->next = NULL;
|
|---|
| 473 |
|
|---|
| 474 | #ifdef TIMER_PROFILE
|
|---|
| 475 | end = uclock();
|
|---|
| 476 | actual = ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000));
|
|---|
| 477 | if (actual < 0)
|
|---|
| 478 | junk = end;
|
|---|
| 479 | TIMERDBG("expected %d ms actual %d ms", event->expected_ms, ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000)));
|
|---|
| 480 | #endif
|
|---|
| 481 |
|
|---|
| 482 | // call the event callback function
|
|---|
| 483 | (*(event->func))((timer_t) event, (int)event->arg);
|
|---|
| 484 |
|
|---|
| 485 | /* If the event has been cancelled, do NOT put it back on the queue. */
|
|---|
| 486 | if ( !(event->flags & TFLAG_CANCELLED) ) {
|
|---|
| 487 |
|
|---|
| 488 | // if the event is a recurring event, reset the timer and
|
|---|
| 489 | // find its correct place in the sorted list of events.
|
|---|
| 490 | //
|
|---|
| 491 | if (timerisset(&event->it_interval)) {
|
|---|
| 492 | // event is recurring...
|
|---|
| 493 | //
|
|---|
| 494 | event->it_value = event->it_interval;
|
|---|
| 495 | #ifdef TIMER_PROFILE
|
|---|
| 496 | event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / US_PER_MS);
|
|---|
| 497 | event->start = uclock();
|
|---|
| 498 | #endif
|
|---|
| 499 | timerroundup(&event->it_value, g_granularity);
|
|---|
| 500 |
|
|---|
| 501 | // Now, march down the list, decrementing the new timer by the
|
|---|
| 502 | // current delta of each event on the queue.
|
|---|
| 503 | ppevent = &event_queue;
|
|---|
| 504 | while (*ppevent) {
|
|---|
| 505 | if ( timercmp(&(event->it_value), &((*ppevent)->it_value), <) ) {
|
|---|
| 506 | // if the proposed event will trigger sooner than the next event
|
|---|
| 507 | // in the queue, we will insert the new event just before the next one.
|
|---|
| 508 | //
|
|---|
| 509 | // we also need to adjust the delta value to the next event.
|
|---|
| 510 | timersub(&((*ppevent)->it_value), &(event->it_value), &((*ppevent)->it_value));
|
|---|
| 511 | break;
|
|---|
| 512 | }
|
|---|
| 513 | timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value));
|
|---|
| 514 | ppevent = &((*ppevent)->next);
|
|---|
| 515 | }
|
|---|
| 516 |
|
|---|
| 517 | // we have found our proper place in the queue,
|
|---|
| 518 | // link our new event into the pending event queue.
|
|---|
| 519 | event->next = *ppevent;
|
|---|
| 520 | *ppevent = event;
|
|---|
| 521 | } else {
|
|---|
| 522 | // there is no interval, so recycle the event structure.
|
|---|
| 523 | //timer_delete((timer_t) event);
|
|---|
| 524 | }
|
|---|
| 525 | }
|
|---|
| 526 |
|
|---|
| 527 | check_event_queue();
|
|---|
| 528 |
|
|---|
| 529 | } while (event_queue && timercmp(&event_queue->it_value, &small_interval, <));
|
|---|
| 530 |
|
|---|
| 531 | // re-issue the timer...
|
|---|
| 532 | if (event_queue) {
|
|---|
| 533 | timerroundup(&event_queue->it_value, g_granularity);
|
|---|
| 534 |
|
|---|
| 535 | timerclear(&itimer.it_interval);
|
|---|
| 536 | itimer.it_value = event_queue->it_value;
|
|---|
| 537 | // we want to be sure to never turn off the timer completely,
|
|---|
| 538 | // so if the next interval is zero, set it to some small value.
|
|---|
| 539 | if (!timerisset(&(itimer.it_value)))
|
|---|
| 540 | itimer.it_value = (struct timeval) { 0, 1 };
|
|---|
| 541 |
|
|---|
| 542 | setitimer(ITIMER_REAL, &itimer, NULL);
|
|---|
| 543 | check_timer();
|
|---|
| 544 | } else {
|
|---|
| 545 | TIMERDBG("There are no events in the queue - timer not reset.");
|
|---|
| 546 | }
|
|---|
| 547 |
|
|---|
| 548 | unblock_timer();
|
|---|
| 549 | }
|
|---|
| 550 |
|
|---|
| 551 | static int block_count = 0;
|
|---|
| 552 |
|
|---|
| 553 | void block_timer()
|
|---|
| 554 | {
|
|---|
| 555 | sigset_t set;
|
|---|
| 556 |
|
|---|
| 557 | if (block_count++ == 0) {
|
|---|
| 558 | sigemptyset(&set);
|
|---|
| 559 | sigaddset(&set, SIGALRM);
|
|---|
| 560 | sigprocmask(SIG_BLOCK, &set, NULL);
|
|---|
| 561 | }
|
|---|
| 562 | }
|
|---|
| 563 |
|
|---|
| 564 | void unblock_timer()
|
|---|
| 565 | {
|
|---|
| 566 | sigset_t set;
|
|---|
| 567 |
|
|---|
| 568 | if (--block_count == 0) {
|
|---|
| 569 | sigemptyset(&set);
|
|---|
| 570 | sigaddset(&set, SIGALRM);
|
|---|
| 571 | sigprocmask(SIG_UNBLOCK, &set, NULL);
|
|---|
| 572 | }
|
|---|
| 573 | }
|
|---|
| 574 |
|
|---|
| 575 | void timer_cancel_all()
|
|---|
| 576 | {
|
|---|
| 577 | struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
|
|---|
| 578 | struct event *event;
|
|---|
| 579 | struct event **ppevent;
|
|---|
| 580 |
|
|---|
| 581 | setitimer(ITIMER_REAL, &timeroff, NULL);
|
|---|
| 582 |
|
|---|
| 583 | ppevent = &event_queue;
|
|---|
| 584 | while (*ppevent) {
|
|---|
| 585 | event = *ppevent;
|
|---|
| 586 | *ppevent = event->next;
|
|---|
| 587 | event->next = NULL;
|
|---|
| 588 | }
|
|---|
| 589 | }
|
|---|
| 590 |
|
|---|
| 591 |
|
|---|
| 592 |
|
|---|
| 593 | void timer_cancel(timer_t timerid)
|
|---|
| 594 | {
|
|---|
| 595 | struct itimerval itimer;
|
|---|
| 596 | struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
|
|---|
| 597 | struct event *event = (struct event *) timerid;
|
|---|
| 598 | struct event **ppevent;
|
|---|
| 599 |
|
|---|
| 600 | if (event->flags & TFLAG_CANCELLED) {
|
|---|
| 601 | TIMERDBG("Cannot cancel a cancelled event");
|
|---|
| 602 | return;
|
|---|
| 603 | }
|
|---|
| 604 |
|
|---|
| 605 | block_timer();
|
|---|
| 606 |
|
|---|
| 607 | ppevent = &event_queue;
|
|---|
| 608 | while (*ppevent) {
|
|---|
| 609 | if ( *ppevent == event ) {
|
|---|
| 610 |
|
|---|
| 611 | /* RACE CONDITION - if the alarm goes off while we are in
|
|---|
| 612 | this loop, and if the timer we want to cancel is the
|
|---|
| 613 | next to expire, the alarm will end up firing
|
|---|
| 614 | after this routine is complete, causing it to go off early. */
|
|---|
| 615 |
|
|---|
| 616 | /* If the cancelled timer is the next to expire,
|
|---|
| 617 | we need to do something special to clean up correctly. */
|
|---|
| 618 | if (event == event_queue && event->next != NULL) {
|
|---|
| 619 | timerclear(&itimer.it_value);
|
|---|
| 620 | getitimer(ITIMER_REAL, &itimer);
|
|---|
| 621 |
|
|---|
| 622 | /* subtract the time that has already passed while waiting for this timer... */
|
|---|
| 623 | timersub(&(event->it_value), &(itimer.it_value), &(event->it_value));
|
|---|
| 624 |
|
|---|
| 625 | /* and add any remainder to the next timer in the list */
|
|---|
| 626 | timeradd(&(event->next->it_value), &(event->it_value), &(event->next->it_value));
|
|---|
| 627 | }
|
|---|
| 628 |
|
|---|
| 629 | *ppevent = event->next;
|
|---|
| 630 | event->next = NULL;
|
|---|
| 631 |
|
|---|
| 632 | if (event_queue) {
|
|---|
| 633 | timerroundup(&event_queue->it_value, g_granularity);
|
|---|
| 634 | timerclear(&itimer.it_interval);
|
|---|
| 635 | itimer.it_value = event_queue->it_value;
|
|---|
| 636 |
|
|---|
| 637 | /* We want to be sure to never turn off the timer
|
|---|
| 638 | completely if there are more events on the queue,
|
|---|
| 639 | so if the next interval is zero, set it to some
|
|---|
| 640 | small value. */
|
|---|
| 641 |
|
|---|
| 642 | if (!timerisset(&(itimer.it_value)))
|
|---|
| 643 | itimer.it_value = (struct timeval) { 0, 1 };
|
|---|
| 644 |
|
|---|
| 645 | assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity);
|
|---|
| 646 | assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= g_granularity);
|
|---|
| 647 | setitimer(ITIMER_REAL, &itimer, NULL);
|
|---|
| 648 | check_timer();
|
|---|
| 649 | } else {
|
|---|
| 650 | setitimer(ITIMER_REAL, &timeroff, NULL);
|
|---|
| 651 | }
|
|---|
| 652 | break;
|
|---|
| 653 | }
|
|---|
| 654 | ppevent = &((*ppevent)->next);
|
|---|
| 655 | }
|
|---|
| 656 |
|
|---|
| 657 | event->flags |= TFLAG_CANCELLED;
|
|---|
| 658 |
|
|---|
| 659 | unblock_timer();
|
|---|
| 660 | }
|
|---|
| 661 |
|
|---|
| 662 | /*
|
|---|
| 663 | * timer related headers
|
|---|
| 664 | */
|
|---|
| 665 | #include "bcmtimer.h"
|
|---|
| 666 |
|
|---|
| 667 | /*
|
|---|
| 668 | * locally used global variables and constants
|
|---|
| 669 | */
|
|---|
| 670 |
|
|---|
| 671 | /*
|
|---|
| 672 | * Initialize internal resources used in the timer module. It must be called
|
|---|
| 673 | * before any other timer function calls. The param 'timer_entries' is used
|
|---|
| 674 | * to pre-allocate fixed number of timer entries.
|
|---|
| 675 | */
|
|---|
| 676 | int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id)
|
|---|
| 677 | {
|
|---|
| 678 | init_event_queue(timer_entries);
|
|---|
| 679 | *module_id = (bcm_timer_module_id)event_freelist;
|
|---|
| 680 | return 0;
|
|---|
| 681 | }
|
|---|
| 682 |
|
|---|
| 683 | /*
|
|---|
| 684 | * Cleanup internal resources used by this timer module. It deletes all
|
|---|
| 685 | * pending timer entries from the backend timer system as well.
|
|---|
| 686 | */
|
|---|
| 687 | int bcm_timer_module_cleanup(bcm_timer_module_id module_id)
|
|---|
| 688 | {
|
|---|
| 689 | module_id = 0;
|
|---|
| 690 | return 0;
|
|---|
| 691 | }
|
|---|
| 692 |
|
|---|
| 693 | /* Enable/Disable timer module */
|
|---|
| 694 | int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable)
|
|---|
| 695 | {
|
|---|
| 696 | if (enable)
|
|---|
| 697 | unblock_timer();
|
|---|
| 698 | else
|
|---|
| 699 | block_timer();
|
|---|
| 700 | return 0;
|
|---|
| 701 | }
|
|---|
| 702 |
|
|---|
| 703 | int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id)
|
|---|
| 704 | {
|
|---|
| 705 | module_id = 0;
|
|---|
| 706 | return timer_create(CLOCK_REALTIME, NULL, (timer_t *)timer_id);
|
|---|
| 707 | }
|
|---|
| 708 |
|
|---|
| 709 | int bcm_timer_delete(bcm_timer_id timer_id)
|
|---|
| 710 | {
|
|---|
| 711 | return timer_delete((timer_t)timer_id);
|
|---|
| 712 | }
|
|---|
| 713 |
|
|---|
| 714 | int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec)
|
|---|
| 715 | {
|
|---|
| 716 | return -1;
|
|---|
| 717 | }
|
|---|
| 718 |
|
|---|
| 719 | int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
|
|---|
| 720 | {
|
|---|
| 721 | return timer_settime((timer_t)timer_id, 0, timer_spec, NULL);
|
|---|
| 722 | }
|
|---|
| 723 |
|
|---|
| 724 | int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data)
|
|---|
| 725 | {
|
|---|
| 726 | return timer_connect((timer_t)timer_id, (void *)func, data);
|
|---|
| 727 | }
|
|---|
| 728 |
|
|---|
| 729 | int bcm_timer_cancel(bcm_timer_id timer_id)
|
|---|
| 730 | {
|
|---|
| 731 | timer_cancel((timer_t)timer_id);
|
|---|
| 732 | return 0;
|
|---|
| 733 | }
|
|---|
| 734 | int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
|
|---|
| 735 | {
|
|---|
| 736 | timer_change_settime((timer_t)timer_id, timer_spec);
|
|---|
| 737 | return 1;
|
|---|
| 738 | }
|
|---|