/* * timer.cc * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: timer.cc,v 1.12 2004/08/22 20:57:30 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include "kernel.h" #include "timer.h" #include "debug.h" #include "config.h" #include "babd.h" #define NUM_TIMER_LISTS 0x100 /* must be a power of 2 */ CTimer *timer_lists[NUM_TIMER_LISTS]; static unsigned long long last_timer_run; extern void dump_timer_list(int i, CTimer *timer_list) { for (CTimer *cur = timer_list; cur; cur = cur->next) { fprintf(stderr, "[%3d] timer %ld.%06ld @ %p\n", i, cur->etv.tv.tv_sec, cur->etv.tv.tv_usec, cur); if (cur->next == timer_list) break; } } void timer_dump(void) { for (int i=0; ietv < tv) { unsigned long long delta; tv = timer_list->etv; delta = tv.scalar() - last_timer_run; // if this offset hasn't wrapped, it must be the lowest. use -1 just in case if (delta < ((NUM_TIMER_LISTS-1) * MOD_SCALAR_DIV)) { if (debug >= 0xffff) fprintf(stderr, "timer_getdelay: idx = %u timer = %p\n", idx, timer_list); break; } } idx ++; idx %= NUM_TIMER_LISTS; } if (tv <= now) { if (debug >= 0xffff) fprintf(stderr, "tv(%ld.%06ld) <= now(%ld.%02ld)\n", tv.tv.tv_sec, tv.tv.tv_usec, now.tv.tv_sec, now.tv.tv_usec ); return (struct timeval){ 0, 0 }; } tmp = tv - now; if (debug >= 0xffff) fprintf(stderr, "tmp(%ld.%06ld) vs now(%ld.%06ld)\n", tv.tv.tv_sec, tv.tv.tv_usec, now.tv.tv_sec, now.tv.tv_usec ); if (tmp.scalar() > NUM_TIMER_LISTS * MOD_SCALAR_DIV / 2) tmp.Set(NUM_TIMER_LISTS * MOD_SCALAR_DIV / 2); return tmp.tv; } void CTimer::TimerExpired(void) { if (cbf) cbf(cbd); } void CTimer::insert_into_timer_list(void) { static unsigned long num_iter, num_inserts; if (next) remove_from_timer_list(); my_timer_list = which_timer_list(etv); if (*my_timer_list) { CTimer **wherep = my_timer_list; while ((*wherep)->etv <= etv) { num_iter++; wherep = &(*wherep)->next; if (*wherep == *my_timer_list) break; } num_inserts++; if (!(num_inserts & 4095)) fprintf(stderr, "inserts: %6lu iter: %6lu av: %4lu\n", num_inserts, num_iter, num_iter/num_inserts); next = *wherep; prev = next->prev; next->prev = this; prev->next = this; *wherep = this; } else *my_timer_list = next = prev = this; if (debug >= 0xffff) { fprintf(stderr, "inserted %p\n", this); timer_dump(); } } void CTimer::remove_from_timer_list(void) { if (!my_timer_list) return; if (debug >= 0xffff) fprintf(stderr, "removed %p\n", this); next->prev = prev; prev->next = next; if (*my_timer_list == this) *my_timer_list = next; if (*my_timer_list == this) *my_timer_list = NULL; next = prev = NULL; my_timer_list = NULL; } extern void run_timer_list(TimeVal *now, CTimer **timer_listp) { CTimer *cur; while ((cur = *timer_listp) && cur->etv <= *now) { if (!cur->active) { fprintf(stderr, "timer bug: !active on list\n"); *(char *)0 = 0; } //Log(LF_DEBUG|LF_IPC, "timer_run: expired timer %p@%ld.%06ld", cur, cur->etv.tv.tv_sec, cur->etv.tv.tv_usec); cur->set = 0; cur->active = 0; cur->remove_from_timer_list(); cur->TimerExpired(); } } extern void timer_run(void) { unsigned long long delta; unsigned i, first, num; TimeVal now; now.GetTimeOfDay(); first = timer_list_mod(last_timer_run); delta = last_timer_run; last_timer_run = now.scalar(); delta = last_timer_run - delta; if (delta >= NUM_TIMER_LISTS * MOD_SCALAR_DIV) { fprintf(stderr, "timer wrapped: %8Ld\n", delta); num = NUM_TIMER_LISTS; } else { num = delta / MOD_SCALAR_DIV + 1; } if (debug >= 0xffff) fprintf(stderr, "timer_run: first = %u num = %u\n", first, num); for (i=0; i