/************************************************************************** * event.c -- Flight Gear periodic event scheduler * * Written by Curtis Olson, started December 1997. * * Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ * (Log is kept at end of this file) **************************************************************************/ #include #include #ifdef USE_FTIME # include # include /* for ftime() and struct timeb */ #else # include /* for get/setitimer, gettimeofday, struct timeval */ #endif /* USE_FTIME */ #include "event.h" #define MAX_EVENTS 100 /* size of event table */ #define MAX_RUN_QUEUE 100 /* size of run queue */ struct fgEVENT { char description[256]; void (*event)(); /* pointer to function */ int status; /* status flag */ long interval; /* interval in ms between each iteration of this event */ #ifdef USE_FTIME struct timeb last_run; /* absolute time for last run */ struct timeb current; /* current time */ struct timeb next_run; /* absolute time for next run */ #else struct timeval last_run; /* absolute time for last run */ struct timeval current; /* current time */ struct timeval next_run; /* absolute time for next run */ struct timezone tz; #endif /* USE_FTIME */ long cum_time; /* cumulative processor time of this event */ long min_time; /* time of quickest execution */ long max_time; /* time of slowest execution */ long count; /* number of times executed */ }; /* Event table */ struct fgEVENT events[MAX_EVENTS]; int event_ptr; /* Run Queue */ int queue[MAX_RUN_QUEUE]; int queue_front; int queue_end; /* initialize the run queue */ void initq() { queue_front = queue_end = 0; } /* return queue empty status */ int emptyq() { if ( queue_front == queue_end ) { return(1); } else { return(0); } } /* return queue full status */ int fullq() { if ( (queue_end + 1) % MAX_RUN_QUEUE == queue_front ) { return(1); } else { return(0); } } /* add a member to the back of the queue */ void addq(int ptr) { if ( !fullq() ) { queue[queue_end] = ptr; events[ptr].status = FG_EVENT_QUEUED; queue_end = (queue_end + 1) % MAX_RUN_QUEUE; } else { printf("RUN QUEUE FULL!!!\n"); } /* printf("Queued function %d (%d %d)\n", ptr, queue_front, queue_end); */ } /* remove a member from the front of the queue */ int popq() { int ptr; if ( !emptyq() ) { ptr = queue[queue_front]; /* printf("Popped position %d = %d\n", queue_front, ptr); */ queue_front = (queue_front + 1) % MAX_RUN_QUEUE; return(ptr); } else { printf("PANIC: RUN QUEUE IS EMPTY!!!\n"); exit(0); } } /* run a specified event */ void fgEventRun(int ptr) { struct fgEVENT *e; long duration; e = &events[ptr]; printf("Running %s\n", e->description); /* record starting time */ #ifdef USE_FTIME ftime(&e->last_run); #else gettimeofday(&e->last_run, &e->tz); #endif /* USE_FTIME */ /* run the event */ (*e->event)(); /* increment the counter for this event */ e->count++; /* update the event status */ e->status = FG_EVENT_READY; /* calculate duration and stats */ #ifdef USE_FTIME ftime(&e->current); duration = 1000 * (e->current.time - e->last_run.time) + (e->current.millitm - e->last_run.millitm); #else gettimeofday(&e->current, &e->tz); duration = 1000000 * (e->current.tv_sec - e->last_run.tv_sec) + (e->current.tv_usec - e->last_run.tv_usec); duration /= 1000; /* convert back to milleseconds */ #endif /* USE_FTIME */ e->cum_time += duration; if ( duration < e->min_time ) { e->min_time = duration; } if ( duration > e->max_time ) { e->max_time = duration; } /* determine the next absolute run time */ #ifdef USE_FTIME e->next_run.time = e->last_run.time + (e->last_run.millitm + e->interval) / 1000; e->next_run.millitm = (e->last_run.millitm + e->interval) % 1000; #else e->next_run.tv_sec = e->last_run.tv_sec + (e->last_run.tv_usec + e->interval * 1000) / 1000000; e->next_run.tv_usec = (e->last_run.tv_usec + e->interval * 1000) % 1000000; #endif /* USE_FTIME */ } /* Initialize the scheduling subsystem */ void fgEventInit() { printf("Initializing event manager\n"); event_ptr = 0; initq(); } /* Register an event with the scheduler, returns a pointer into the * event table */ int fgEventRegister(char *desc, void (*event)(), int status, int interval) { struct fgEVENT *e; e = &events[event_ptr]; printf("Registering event: %s\n", desc); if ( strlen(desc) < 256 ) { strcpy(e->description, desc); } else { strncpy(e->description, desc, 255); e->description[255] = '\0'; } e->event = event; e->status = status; e->interval = interval; e->cum_time = 0; e->min_time = 100000; e->max_time = 0; e->count = 0; /* Actually run the event */ fgEventRun(event_ptr); event_ptr++; return(event_ptr - 1); } /* Update the scheduling parameters for an event */ void fgEventUpdate() { } /* Delete a scheduled event */ void fgEventDelete() { } /* Temporarily suspend scheduling of an event */ void fgEventSuspend() { } /* Resume scheduling and event */ void fgEventResume() { } /* Dump scheduling stats */ void fgEventPrintStats() { int i; if ( event_ptr > 0 ) { printf("\n"); printf("Event Stats\n"); printf("-----------\n"); for ( i = 0; i < event_ptr; i++ ) { printf(" %-20s int=%.2fs cum=%ld min=%ld max=%ld count=%ld ave=%.2f\n", events[i].description, events[i].interval / 1000.0, events[i].cum_time, events[i].min_time, events[i].max_time, events[i].count, events[i].cum_time / (double)events[i].count); } printf("\n"); } } /* Add pending jobs to the run queue and run the job at the front of * the queue */ void fgEventProcess() { #ifdef USE_FTIME struct timeb current; #else struct timeval current; struct timezone tz; #endif /* USE_FTIME */ int i; printf("Processing events\n"); /* get the current time */ #ifdef USE_FTIME ftime(¤t); #else gettimeofday(¤t, &tz); #endif /* USE_FTIME */ /* printf("Checking if anything is ready to move to the run queue\n"); */ /* see if anything else is ready to be placed on the run queue */ for ( i = 0; i < event_ptr; i++ ) { if ( events[i].status == FG_EVENT_READY ) { #ifdef USE_FTIME if ( current.time > events[i].next_run.time ) { addq(i); } else if ( (current.time == events[i].next_run.time) && (current.millitm >= events[i].next_run.millitm) ) { addq(i); } #else if ( current.tv_sec > events[i].next_run.tv_sec ) { addq(i); } else if ( (current.tv_sec == events[i].next_run.tv_sec) && (current.tv_usec >= events[i].next_run.tv_usec) ) { addq(i); } #endif /* USE_FTIME */ } } /* Checking to see if there is anything on the run queue */ /* printf("Checking to see if there is anything on the run queue\n"); */ if ( !emptyq() ) { /* printf("Yep, running it\n"); */ i = popq(); fgEventRun(i); } } /* $Log$ /* Revision 1.5 1998/01/06 01:20:27 curt /* Tweaks to help building with MSVC++ /* * Revision 1.4 1997/12/31 17:46:50 curt * Tweaked fg_time.c to be able to use ftime() instead of gettimeofday() * * Revision 1.3 1997/12/30 22:22:42 curt * Further integration of event manager. * * Revision 1.2 1997/12/30 20:47:58 curt * Integrated new event manager with subsystem initializations. * * Revision 1.1 1997/12/30 04:19:22 curt * Initial revision. * */