/* IRCfs - IRC FileServ for *nix. * Copyright (C) 2002 Nick 'Zaf' Clifford * For licensing details, refer to the LICENSE file in the source * code directory. */ #include #include #include "runtime.h" #include "module.h" #include "misc.h" #include "select.h" #include "timer.h" #include "list.h" #include "magic.h" struct timer { MAGIC magic; POOL pool; time_t duration; time_t set; int elapsed; /* 1 if the timer has elapsed */ timer_callback_t callback; void *appdata; unsigned long num; }; struct timer_search { timer_callback_t callback; void *appdata; }; static int timer_compare_callback(void *what, void *node); static int timer_compare_both(void *what, void *node); static void timer_select_pre_func(void); static void timer_select_post_func(void); static void timer_is_ready(struct timer *t); static int timer_is_soonest(struct list *l, struct timer *t, time_t *soonest); MODULE_NAME("timer"); MODULE_INIT(timer_init); MODULE_DESTROY(timer_unload); MODULE_DEPENDS("select"); MODULE_REGISTER(timer); DEBUG timer_debug; static POOL timer_pool; struct list *timer_list; static unsigned long timer_count; int timer_init() { timer_debug = debug_register("timer"); timer_pool = pool_new(NULL); timer_list = list_create(timer_pool); register_pre_func(timer_select_pre_func); register_post_func(timer_select_post_func); return 0; } int timer_unload() { list_destroy(timer_list); pool_destroy(timer_pool); return 0; } struct timer *timer_start(time_t howlong, timer_callback_t cb, void *appdata) { struct timer *t; struct timeval tv; POOL p; if (howlong <= 0 || cb == NULL) { return NULL; } p = pool_new(timer_pool); t = (struct timer *) palloc(p,sizeof(struct timer)); if (t == NULL) return NULL; set_magic(t, MAGIC_TIMER); if (gettimeofday(&tv,NULL) == -1) { error(timer_debug,"gettimeofday() returned -1"); return NULL; } timer_count++; t->pool = p; t->set = tv.tv_sec; t->duration = howlong; t->elapsed = 0; t->callback = cb; t->appdata = appdata; t->num = timer_count; info(timer_debug,"Timer(%d) created. Elapse in %ld seconds, " "function=%p appdata=%p", t->num, howlong, cb, appdata); list_add_end(timer_list,t); return t; } time_t timer_reset(struct timer *t) { struct timeval tv; ASSERT(is_magic(t,MAGIC_TIMER)); if (gettimeofday(&tv,NULL) == -1) { error(timer_debug,"gettimeofday() returned -1"); return -1; } info(timer_debug,"Timer(%d) reset. (duration=%ld)", t->num, t->duration); t->set = tv.tv_sec; t->elapsed = 0; return t->duration; } time_t timer_restart(struct timer *t, time_t howlong) { struct timeval tv; ASSERT(is_magic(t,MAGIC_TIMER)); if (gettimeofday(&tv,NULL) == -1) { error(timer_debug,"gettimeofday() returned -1"); return -1; } t->set = tv.tv_sec; t->elapsed = 0; t->duration = howlong; info(timer_debug,"Timer(%d) restart (duration=%ld)", t->num, t->duration); return t->duration; } /* Stops the specified timer */ int timer_stop(struct timer *t) { ASSERT(is_magic(t,MAGIC_TIMER)); t->set = 0; if (list_find_data(timer_list,t) == NULL) { error(timer_debug,"Can't find specified timer"); return -1; } info(timer_debug,"Timer(%d) stopped",t->num); list_del_current(timer_list); return 0; } int timer_compare_callback(void *what, void *node) { struct timer *t = (struct timer *) node; timer_callback_t cb = (timer_callback_t) what; ASSERT(is_magic(t,MAGIC_TIMER)); if (t->callback == cb) return 1; return 0; } int timer_compare_both(void *what, void *node) { struct timer *t = (struct timer *) node; struct timer_search *ts = (struct timer_search *) what; ASSERT(is_magic(t,MAGIC_TIMER)); if (t->callback == ts->callback && t->appdata == ts->appdata) return 1; return 0; } /* Searchs through the timer list for a timer that calls the specified * callback */ struct timer *timer_search(timer_callback_t cb) { struct timer *t; t = (struct timer *) list_find(timer_list, cb, timer_compare_callback); return t; } /* Searchs through the timer list for a timer that calls the specified * callback AND has an appdata with the same value */ struct timer *timer_search2(timer_callback_t cb, void *appdata) { struct timer *t; struct timer_search ts; ts.callback = cb; ts.appdata = appdata; t = (struct timer *) list_find(timer_list, &ts, timer_compare_both); return t; } /* Returns the appdata set when the timer was created. */ void *timer_get_appdata(struct timer *t) { ASSERT(is_magic(t,MAGIC_TIMER)); return t->appdata; } /* Returns the duration specified for the timer. See timer_get_time_left * for how long until the timer times out. */ time_t timer_get_duration(struct timer *t) { ASSERT(is_magic(t,MAGIC_TIMER)); return t->duration; } /* Returns how many seconds left until the timer times out. (See notes * for timer_start on the timing interval */ time_t timer_get_time_left(struct timer *t) { struct timeval tv; signed long left; ASSERT(is_magic(t,MAGIC_TIMER)); if (gettimeofday(&tv,NULL) == -1) { error(timer_debug,"gettimeofday() returned -1"); return -1; } /* Return -1 if disabled/paused */ if (t->set == 0) { return -1; } left = t->duration - (tv.tv_sec - t->set); if (left < 0) left = 0; return (time_t) left; } /* Returns a pointer to the callback function that will be called when * the timer times out */ timer_callback_t timer_get_callback(struct timer *t) { ASSERT(is_magic(t,MAGIC_TIMER)); return t->callback; } static int timer_is_soonest(struct list *l, struct timer *t, time_t *soonest) { time_t when; when = timer_get_time_left(t); if (when == -1) { /* Do nothing */ } else if (when == 0) { timer_is_ready(t); } else if (*soonest == -1 || when < *soonest) { *soonest = when; } return 0; } void timer_destroy(struct timer *t) { ASSERT(is_magic(t, MAGIC_TIMER)); if (list_find_data(timer_list,t) == NULL) { return; } info(timer_debug,"Timer(%d) destroying", t->num); list_del_current(timer_list); pool_destroy(t->pool); } static void timer_is_ready(struct timer *t) { if (timer_get_time_left(t) != 0) return; info(timer_debug,"Timer(%d) elapsed",t->num); t->set = 0; t->callback(t, t->appdata); if (list_find_data(timer_list,t) == NULL) { /* Timer has already been destroyed */ return; } if (t->set != 0) { /* Timer has been reset */ return; } timer_destroy(t); return; } static void timer_select_pre_func(void) { time_t soonest=-1; debug(timer_debug,"Finding next soonest timer"); /* Find when the next event will be */ list_foreach(timer_list, (list_callback_t) timer_is_soonest, &soonest); debug(timer_debug,"Next soonest timer is %ld",soonest); if (soonest == -1) { select_set_timeout(select_timeout_dont_care); } else { select_set_timeout(soonest); } return; } static void timer_select_post_func(void) { /* Go through each timer, find the ones expired, and execute * them */ debug(timer_debug,"Checking for timers that have elapsed"); list_foreach_simple(timer_list, (list_simple_callback_t)timer_is_ready); return; }