X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Futil%2Ftimer.c;h=8ffadc88d829b9d05453c7bea2c7efd615ec96bd;hb=b9ceca0af9fbad70fe14f087ccdda72d8c7e7cf6;hp=d66fa9927cad203a0464e4f8f56f82bc074cb15b;hpb=b0088891ec08706a92184aff508c2baefe488b54;p=chaz%2Ftint2 diff --git a/src/util/timer.c b/src/util/timer.c index d66fa99..8ffadc8 100644 --- a/src/util/timer.c +++ b/src/util/timer.c @@ -15,68 +15,162 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************/ -#include +#include #include -#include -#include #include "timer.h" -GSList* timer_list = 0; +GSList* timeout_list = 0; +struct timespec next_timeout; -int install_timer(int value_sec, int value_nsec, int interval_sec, int interval_nsec, void (*_callback)()) -{ - if ( value_sec < 0 || interval_sec < 0 || _callback == 0 ) - return -1; +void add_timeout_intern(int value_msec, int interval_msec, void(*_callback)(), struct timeout* t); +gint compare_timeouts(gconstpointer t1, gconstpointer t2); +gint compare_timespecs(const struct timespec* t1, const struct timespec* t2); +int timespec_subtract(struct timespec* result, struct timespec* x, struct timespec* y); - int timer_fd; - struct itimerspec timer; - timer.it_value = (struct timespec){ .tv_sec=value_sec, .tv_nsec=value_nsec }; - timer.it_interval = (struct timespec){ .tv_sec=interval_sec, .tv_nsec=interval_nsec }; - timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); - timerfd_settime(timer_fd, 0, &timer, 0); - struct timer* t = malloc(sizeof(struct timer)); - t->id=timer_fd; - t->_callback = _callback; - timer_list = g_slist_prepend(timer_list, t); - int flags = fcntl( timer_fd, F_GETFL, 0 ); - if( flags != -1 ) - fcntl( timer_fd, F_SETFL, flags | O_NONBLOCK ); +/** Implementation notes for timeouts: The timeouts are kept in a GSList sorted by their + * expiration time. + * That means that update_next_timeout() only have to consider the first timeout in the list, + * and callback_timeout_expired() only have to consider the timeouts as long as the expiration time + * is in the past to the current time. + * As time measurement we use clock_gettime(CLOCK_MONOTONIC) because this refers to a timer, which + * reference point lies somewhere in the past and cannot be changed, but just queried. +**/ - return timer_fd; +const struct timeout* add_timeout(int value_msec, int interval_msec, void (*_callback)()) +{ + struct timeout* t = malloc(sizeof(struct timeout)); + add_timeout_intern(value_msec, interval_msec, _callback, t); + return t; } -void reset_timer(int id, int value_sec, int value_nsec, int interval_sec, int interval_nsec) +void change_timeout(const struct timeout *t, int value_msec, int interval_msec, void(*_callback)()) { - struct itimerspec timer; - timer.it_value = (struct timespec){ .tv_sec=value_sec, .tv_nsec=value_nsec }; - timer.it_interval = (struct timespec){ .tv_sec=interval_sec, .tv_nsec=interval_nsec }; - timerfd_settime(id, 0, &timer, 0); + timeout_list = g_slist_remove(timeout_list, t); + add_timeout_intern(value_msec, interval_msec, _callback, (struct timeout*)t); } -void uninstall_timer(int id) +void update_next_timeout() { - close(id); - GSList* timer_iter = timer_list; - while (timer_iter) { - struct timer* t = timer_iter->data; - if (t->id == id) { - timer_list = g_slist_remove(timer_list, t); - free(t); - return; + if (timeout_list) { + struct timeout* t = timeout_list->data; + struct timespec cur_time; + clock_gettime(CLOCK_MONOTONIC, &cur_time); + if (timespec_subtract(&next_timeout, &t->timeout_expires, &cur_time)) { + next_timeout.tv_sec = 0; + next_timeout.tv_nsec = 0; + } + } + else + next_timeout.tv_sec = -1; +} + + +void callback_timeout_expired() +{ + struct timespec cur_time; + struct timeout* t; + while (timeout_list) { + clock_gettime(CLOCK_MONOTONIC, &cur_time); + t = timeout_list->data; + if (compare_timespecs(&t->timeout_expires, &cur_time) <= 0) { + // it's time for the callback function + t->_callback(); + if (g_slist_find(timeout_list, t)) { + // if _callback() calls stop_timeout(t) the timeout 't' was freed and is not in the timeout_list + timeout_list = g_slist_remove(timeout_list, t); + if (t->interval_msec > 0) + add_timeout_intern(t->interval_msec, t->interval_msec, t->_callback, t); + else + free(t); + } } - timer_iter = timer_iter->next; + else + return; + } +} + + +void stop_timeout(const struct timeout* t) +{ + // if not in the list, it was deleted in callback_timeout_expired + if (g_slist_find(timeout_list, t)) { + timeout_list = g_slist_remove(timeout_list, t); + free((void*)t); + } +} + + +void stop_all_timeouts() +{ + while (timeout_list) { + free(timeout_list->data); + timeout_list = g_slist_remove(timeout_list, timeout_list->data); + } +} + + +void add_timeout_intern(int value_msec, int interval_msec, void(*_callback)(), struct timeout *t) +{ + t->interval_msec = interval_msec; + t->_callback = _callback; + struct timespec expire; + clock_gettime(CLOCK_MONOTONIC, &expire); + expire.tv_sec += value_msec / 1000; + expire.tv_nsec += (value_msec % 1000)*1000000; + if (expire.tv_nsec >= 1000000000) { // 10^9 + expire.tv_sec++; + expire.tv_nsec -= 1000000000; } + t->timeout_expires = expire; + timeout_list = g_slist_insert_sorted(timeout_list, t, compare_timeouts); +} + + +gint compare_timeouts(gconstpointer t1, gconstpointer t2) +{ + return compare_timespecs(&((const struct timeout*)t1)->timeout_expires, + &((const struct timeout*)t2)->timeout_expires); } -void uninstall_all_timer() +gint compare_timespecs(const struct timespec* t1, const struct timespec* t2) +{ + if (t1->tv_sec < t2->tv_sec) + return -1; + else if (t1->tv_sec == t2->tv_sec) { + if (t1->tv_nsec < t2->tv_nsec) + return -1; + else if (t1->tv_nsec == t2->tv_nsec) + return 0; + else + return 1; + } + else + return 1; +} + +int timespec_subtract(struct timespec* result, struct timespec* x, struct timespec* y) { - while (timer_list) { - struct timer* t = timer_list->data; - uninstall_timer(t->id); + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1; + y->tv_nsec -= 1000000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_nsec - y->tv_nsec > 1000000000) { + int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000; + y->tv_nsec += 1000000000 * nsec; + y->tv_sec -= nsec; } + + /* Compute the time remaining to wait. tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; }