/* $Header: /CVSROOT/tinolib/old/alarm.h,v 1.6 2007/04/04 05:28:25 tino Exp $ * * Alarm list processing. * * Copyright (C)2006-2007 Valentin Hilbig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Log: alarm.h,v $ * Revision 1.6 2007/04/04 05:28:25 tino * See ChangeLog * * Revision 1.5 2007/04/02 17:13:42 tino * Again some changes, see ChangeLog * * Revision 1.4 2007/04/02 16:51:17 tino * Now shall be able to overcome when time overruns. * Also improved features and optimized sorting. * * Revision 1.3 2007/01/25 05:03:16 tino * See ChangeLog. Added functions and improved alarm() handling * * Revision 1.2 2007/01/25 04:39:15 tino * Unit-Test now work for C++ files, too (and some fixes so that "make test" works). * * Revision 1.1 2006/10/21 01:42:01 tino * added */ #ifndef tino_INC_alarm_h #define tino_INC_alarm_h #include "fatal.h" #include "alloc.h" #include #include /** Private structure */ struct tino_alarm_list { struct tino_alarm_list *next; int seconds; time_t stamp; int (*cb)(void *, long, time_t); void *user; }; /** Private variables */ static struct tino_alarm_list *tino_alarm_list_active, *tino_alarm_list_inactive; static int tino_alarm_pending; static int tino_alarm_watchdog; /** Private alarm handler */ static void tino_alarm_handler(int sig) { if (++tino_alarm_pending>tino_alarm_watchdog && tino_alarm_watchdog) tino_fatal("watchdog"); #ifdef TINO_USE_NO_SIGACTION if (signal(SIGALRM, tino_alarm_handler)) tino_fatal("signal"); #endif alarm(1); } /** Public run alarm shortcut * * Call this macro to run the pending alarms. Do this after routines * (like read() and write()) which can return EINTR. */ #define TINO_ALARM_RUN() do { if (tino_alarm_pending) tino_alarm_run(); } while (0) /** Set the watchdog, disabled when 0 * * The watchdog is something which hits, if the alarm is pending * longer than the given seconds and it is not yet processed. If * there is no active alarm, the watchdog runs each second, such that * you must call TINO_ALARM_RUN() regularily. * * There is no watchdog action (yet). It's always fatal. */ static void tino_alarm_set_watchdog(int watchdog) { tino_alarm_watchdog = watchdog; } /** Sort in new alarms */ static void tino_alarm_sort(struct tino_alarm_list *add) { while (add) { struct tino_alarm_list *ptr, **last; for (last= &tino_alarm_list_active;; last= &ptr->next) { /* Hunt for position to insert. * * New alarms will be appended to all other alarms with the * same timestamp. * * The funny compare (by substraction) is failproof when * time_t runs over. */ if ((ptr= *last)!=0 && ((long)(ptr->stamp-add->stamp))>=0) continue; /* Insert element at current position */ ptr = add; add = add->next; ptr->next = *last; *last = ptr; /* Are we ready? */ if (!add) return; /* If the sequence is broken, that is the next element to * add (*add) must fire before the current added element * (*ptr), then start from all over. */ if (((long)(ptr->stamp-add->stamp))<0) break; } } } /** Run alarms * * If alarm function returns nonzero, the alarm calls will be disabled. * * Convention: * 1 one time alarm * 0 continuous alarm * -1 error in routine (stop alarm) */ static void tino_alarm_run(void) { struct tino_alarm_list *tmp, *ptr, **last; static time_t reference; time_t now; long delta; alarm(0); tino_alarm_pending = 0; time(&now); /* If time runs backward (because you set the time) the alarms must * be corrected. */ if (reference && (delta=reference-now)>0) for (ptr=tino_alarm_list_active; ptr; ptr=ptr->next) ptr->stamp -= delta; reference = now; if (!reference) /* Am I paranoid! */ reference--; /* Run the alarms up to now */ for (last= &tino_alarm_list_active; (ptr= *last)!=0 && (delta=now-ptr->stamp)>=0; ) { ptr->stamp = now+ptr->seconds; if (ptr->cb ? ptr->cb(ptr->user, delta, now) : !!ptr->user) { *last = ptr->next; ptr->next = tino_alarm_list_inactive; tino_alarm_list_inactive = ptr; } else last= &ptr->next; } *last = 0; tmp = tino_alarm_list_active; tino_alarm_list_active = ptr; tino_alarm_sort(tmp); /* Schedule next alarm */ if (tino_alarm_list_active || tino_alarm_watchdog) { #ifdef TINO_USE_NO_SIGACTION if (signal(SIGALRM, tino_alarm_handler)) tino_fatal("signal"); #endif delta = tino_alarm_list_active ? tino_alarm_list_active->stamp-now : 1; if (delta<1) delta = 1; if (delta>1000) delta = 1000; alarm(delta); } } /** Run alarms if pending * * Wrapper function */ static void tino_alarm_run_pending(void) { TINO_ALARM_RUN(); } /** Stop an alarm callback * * Searches callback/user in alarm list and deletes it. * * If user is NULL, any value will fit. * * Important Change: * * tino_alarm_stop(NULL,NULL) no more stops all alarms. Use * tino_alarm_stop_all() for this! * * Note that this does not re-schedule alarms. */ static void tino_alarm_stop(int (*callback)(void *, long, time_t), void *user) { struct tino_alarm_list **last, *ptr; for (last= &tino_alarm_list_active; (ptr= *last)!=0; ) { if (ptr->cb!=callback || (callback && user && ptr->user!=user)) { last = &ptr->next; continue; } *last = ptr->next; ptr->next = tino_alarm_list_inactive; tino_alarm_list_inactive = ptr; } if (!tino_alarm_list_active) tino_alarm_run(); /* reschedule the watchdog */ } /** Stop all alarms * * If called twice this frees all the alarm structures. It does not * unregister the signal handler functions, though. * * Note: If you want to disable the watchdog, too, use following * sequence: * * tino_alarm_set_watchdog(0); * tino_alarm_stop_all(); * tino_alarm_stop_all(); * * This also shall ensure that no more spurious alarms show up in * future. */ static void tino_alarm_stop_all(void) { struct tino_alarm_list *ptr; while ((ptr=tino_alarm_list_inactive)!=0) { tino_alarm_list_inactive = ptr->next; free(ptr); } tino_alarm_list_inactive = tino_alarm_list_active; tino_alarm_list_active = 0; tino_alarm_run(); /* Disable alarm()s */ } /** Set an alarm callback * * The alarm callback function will be run each seconds with the user arg. * * The second argument to the callback function is the number of * seconds elapsed after the alarm() had to run (hopefully always 0). * * If callback is NULL this is an anonymous alarm. If user is NULL * this is continuously generating EINTR signals until you stop it, * else it will just run one time. Note that you can only set one * such alarm (this is to prevent table fills in case of errors)! */ static void tino_alarm_set(int seconds, int (*callback)(void *, long, time_t), void *user) { struct tino_alarm_list *ptr; time_t now; tino_alarm_stop(callback, user); if ((ptr=tino_alarm_list_inactive)==0) ptr = tino_alloc0(sizeof *ptr); else tino_alarm_list_inactive = ptr->next; time(&now); ptr->seconds = seconds; ptr->stamp = now+seconds; ptr->cb = callback; ptr->user = user; #ifndef TINO_USE_NO_SIGACTION if (!tino_alarm_list_active && !tino_alarm_list_inactive) { struct sigaction sa; memset(&sa, 0, sizeof sa); sa.sa_handler = tino_alarm_handler; if (sigaction(SIGALRM, &sa, NULL)) tino_fatal("sigaction"); } #endif tino_alarm_sort(ptr); tino_alarm_run(); } #endif