123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #include <sys/time.h>
- #include <stdlib.h>
- #include <string.h>
- #include "monoclock.h"
- #include "timerqueue.h"
- #include "events.h"
- #include "events_internal.h"
- struct timerrec {
- struct eventrec * r;
- void * cookie;
- struct timeval tv_orig;
- };
- /* This also tracks whether we've initialized the atexit function. */
- static struct timerqueue * Q = NULL;
- static void events_timer_shutdown(void);
- /* Set tv := <current time> + tdelta. */
- static int
- gettimeout(struct timeval * tv, const struct timeval * tdelta)
- {
- if (monoclock_get(tv))
- goto err0;
- tv->tv_sec += tdelta->tv_sec;
- if ((tv->tv_usec += tdelta->tv_usec) >= 1000000) {
- tv->tv_usec -= 1000000;
- tv->tv_sec += 1;
- }
- /* Success! */
- return (0);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * events_timer_register(func, cookie, timeo):
- * Register ${func}(${cookie}) to be run ${timeo} in the future. Return a
- * cookie which can be passed to events_timer_cancel() or events_timer_reset().
- */
- void *
- events_timer_register(int (* func)(void *), void * cookie,
- const struct timeval * timeo)
- {
- struct eventrec * r;
- struct timerrec * t;
- struct timeval tv;
- /* Create the timer queue if it doesn't exist yet. */
- if (Q == NULL) {
- if ((Q = timerqueue_init()) == NULL)
- goto err0;
- /* Clean up the timer queue at exit. */
- if (atexit(events_timer_shutdown))
- goto err0;
- }
- /* Bundle into an eventrec record. */
- if ((r = events_mkrec(func, cookie)) == NULL)
- goto err0;
- /* Create a timer record. */
- if ((t = malloc(sizeof(struct timerrec))) == NULL)
- goto err1;
- t->r = r;
- memcpy(&t->tv_orig, timeo, sizeof(struct timeval));
- /* Compute the absolute timeout. */
- if (gettimeout(&tv, &t->tv_orig))
- goto err2;
- /* Add this to the timer queue. */
- if ((t->cookie = timerqueue_add(Q, &tv, t)) == NULL)
- goto err2;
- /* Success! */
- return (t);
- err2:
- free(t);
- err1:
- events_freerec(r);
- err0:
- /* Failure! */
- return (NULL);
- }
- /**
- * events_timer_register_double(func, cookie, timeo):
- * As events_timer_register(), but ${timeo} is a double-precision
- * floating-point value specifying a number of seconds.
- */
- void *
- events_timer_register_double(int (* func)(void *), void * cookie,
- double timeo)
- {
- struct timeval tv;
- /* Convert timeo to a struct timeval. */
- tv.tv_sec = (time_t)timeo;
- tv.tv_usec = (suseconds_t)((timeo - (double)tv.tv_sec) * 1000000.0);
- /* Schedule the timeout. */
- return (events_timer_register(func, cookie, &tv));
- }
- /**
- * events_timer_cancel(cookie):
- * Cancel the timer for which the cookie ${cookie} was returned by
- * events_timer_register().
- */
- void
- events_timer_cancel(void * cookie)
- {
- struct timerrec * t = cookie;
- /* Remove from the timer queue. */
- timerqueue_delete(Q, t->cookie);
- /* Free the eventrec and timer records. */
- events_freerec(t->r);
- free(t);
- }
- /**
- * events_timer_reset(cookie):
- * Reset the timer for which the cookie ${cookie} was returned by
- * events_timer_register() to its initial value.
- */
- int
- events_timer_reset(void * cookie)
- {
- struct timerrec * t = cookie;
- struct timeval tv;
- /* Compute the new timeout. */
- if (gettimeout(&tv, &t->tv_orig))
- goto err0;
- /* Adjust the timer. */
- timerqueue_increase(Q, t->cookie, &tv);
- /* Success! */
- return (0);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * events_timer_min(timeo):
- * Return via ${timeo} a pointer to the minimum time which must be waited
- * before a timer will expire; or to NULL if there are no timers. The caller
- * is responsible for freeing the returned pointer.
- */
- int
- events_timer_min(struct timeval ** timeo)
- {
- struct timeval tnow;
- const struct timeval * tv;
- /* If we have no queue, we have no timers; return NULL. */
- if (Q == NULL) {
- *timeo = NULL;
- goto done;
- }
- /* Get the minimum timer from the queue. */
- tv = timerqueue_getmin(Q);
- /* If there are no timers, return NULL. */
- if (tv == NULL) {
- *timeo = NULL;
- goto done;
- }
- /* Allocate space for holding the returned timeval. */
- if ((*timeo = malloc(sizeof(struct timeval))) == NULL)
- goto err0;
- /* Get the current time... */
- if (monoclock_get(&tnow))
- goto err1;
- /* ... and compare it to the minimum timer. */
- if ((tnow.tv_sec > tv->tv_sec) ||
- ((tnow.tv_sec == tv->tv_sec) && (tnow.tv_usec > tv->tv_usec))) {
- /* The timer has already expired, so return zero. */
- (*timeo)->tv_sec = 0;
- (*timeo)->tv_usec = 0;
- } else {
- /* Compute the difference. */
- (*timeo)->tv_sec = tv->tv_sec - tnow.tv_sec;
- (*timeo)->tv_usec = tv->tv_usec - tnow.tv_usec;
- if (tv->tv_usec < tnow.tv_usec) {
- (*timeo)->tv_usec += 1000000;
- (*timeo)->tv_sec -= 1;
- }
- }
- done:
- /* Success! */
- return (0);
- err1:
- free(*timeo);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * events_timer_get(r):
- * Return via ${r} a pointer to an eventrec structure corresponding to an
- * expired timer, and delete said timer; or to NULL if there are no expired
- * timers. The caller is responsible for freeing the returned pointer.
- */
- int
- events_timer_get(struct eventrec ** r)
- {
- struct timeval tnow;
- struct timerrec * t;
- /* If we have no queue, we have no timers; return NULL. */
- if (Q == NULL) {
- *r = NULL;
- goto done;
- }
- /* Get current time. */
- if (monoclock_get(&tnow))
- goto err0;
- /* Get an expired timer, if there is one. */
- t = timerqueue_getptr(Q, &tnow);
- /* If there is an expired timer... */
- if (t != NULL) {
- /* ... pass back the eventrec and free the timer. */
- *r = t->r;
- free(t);
- } else {
- /* Otherwise, return NULL. */
- *r = NULL;
- }
- done:
- /* Success! */
- return (0);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * events_timer_shutdown(void):
- * Clean up and free memory. This should run automatically via atexit.
- */
- static void
- events_timer_shutdown(void)
- {
- /* If we have a queue and it is empty, free it. */
- if ((Q != NULL) && (timerqueue_getmin(Q) == NULL)) {
- timerqueue_free(Q);
- Q = NULL;
- }
- }
|