diff options
Diffstat (limited to 'kstars/kstars/indi/eventloop.c')
-rw-r--r-- | kstars/kstars/indi/eventloop.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/kstars/kstars/indi/eventloop.c b/kstars/kstars/indi/eventloop.c new file mode 100644 index 00000000..8df3aca9 --- /dev/null +++ b/kstars/kstars/indi/eventloop.c @@ -0,0 +1,424 @@ +#if 0 + INDI + Copyright (C) 2003 Elwood C. Downey + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#endif + +/* suite of functions to implement an event driven program. + * + * callbacks may be registered that are triggered when a file descriptor + * will not block when read; + * + * timers may be registered that will run no sooner than a specified delay from + * the moment they were registered; + * + * work procedures may be registered that are called when there is nothing + * else to do; + * + #define MAIN_TEST for a stand-alone test program. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> + +#include "eventloop.h" + +/* info about one registered callback. */ +typedef struct { + int in_use; /* flag to make this record is active */ + int fd; /* fd descriptor to watch for read */ + void *ud; /* user's data handle */ + CBF *fp; /* callback function */ + int cid; /* unique id for this callback */ +} CB; + +/* info about one registered timer function */ +typedef struct { + int tgo; /* trigger time, ms from epoch */ + void *ud; /* user's data handle */ + TCF *fp; /* timer function */ + int tid; /* unique id for this timer */ +} TF; + +/* info about one registered work procedure. */ +typedef struct { + int in_use; /* flag to make this record is active */ + void *ud; /* user's data handle */ + WPF *fp; /* work proc function function */ + int wid; /* unique id for this work proc */ +} WP; + + +static CB *cback; /* malloced list of callbacks */ +static int ncback; /* n entries in cback[] */ +static int cid; /* source of callback ids */ + +static TF *timef; /* malloced list of timer functions */ +static int ntimef; /* n entries in ntimef[] */ +static struct timeval epoch; /* arbitrary t0 */ +static int tid; /* source of timer ids */ +#define EPDT(tp) /* ms from epoch to timeval *tp */ \ + (((tp)->tv_sec-epoch.tv_sec)*1000 + ((tp)->tv_usec-epoch.tv_usec)/1000) + +static WP *wproc; /* malloced list of work procedures */ +static int nwproc; /* n entries in wproc[] */ +static int nwpinuse; /* n entries in wproc[] marked in-use */ +static int wid; /* source of worproc ids */ + +static void runWorkProcs (void); +static void callCallbacks(fd_set *rfdp, int nready); +static void popTimers(); +static void oneLoop(void); + +/* inf loop to dispatch callbacks, work procs and timers as necessary. + * never returns. + */ +void +eventLoop() +{ + /* init epoch to now */ + gettimeofday (&epoch, NULL); + + /* run loop forever */ + while (1) + oneLoop(); +} + +/* register a new callback, fp, to be called with ud as arg when fd is ready. + * return a unique callback id for use with rmCallback(). + */ +int +addCallback (int fd, CBF *fp, void *ud) +{ + CB *cp; + + for (cp = cback; cp < &cback[ncback]; cp++) + if (!cp->in_use) + break; + + if (cp == &cback[ncback]) { + cback = cback ? (CB *) realloc (cback, (ncback+1)*sizeof(CB)) + : (CB *) malloc (sizeof(CB)); + cp = &cback[ncback++]; + } + + cp->in_use = 1; + cp->fp = fp; + cp->ud = ud; + cp->fd = fd; + return (cp->cid = ++cid); +} + +/* remove the callback with the given id, as returned from addCallback(). + * silently ignore if id not found. + */ +void +rmCallback (int callbackid) +{ + CB *cp; + + for (cp = cback; cp < &cback[ncback]; cp++) { + if (cp->in_use && cp->cid == callbackid) { + cp->in_use = 0; + break; + } + } +} + +/* register a new timer function, fp, to be called with ud as arg after ms + * milliseconds. add to list in order of decreasing time from epoch, ie, + * last entry runs soonest. return id for use with rmTimer(). + */ +int +addTimer (int ms, TCF *fp, void *ud) +{ + struct timeval t; + TF *tp; + + gettimeofday (&t, NULL); + + timef = timef ? (TF *) realloc (timef, (ntimef+1)*sizeof(TF)) + : (TF *) malloc (sizeof(TF)); + tp = &timef[ntimef++]; + + tp->ud = ud; + tp->fp = fp; + tp->tgo = EPDT(&t) + ms; + + for ( ; tp > timef && tp[0].tgo > tp[-1].tgo; tp--) { + TF tmptf = tp[-1]; + tp[-1] = tp[0]; + tp[0] = tmptf; + } + + return (tp->tid = ++tid); +} + +/* remove the timer with the given id, as returned from addTimer(). + * silently ignore if id not found. + */ +void +rmTimer (int timerID) +{ + TF *tp; + + /* find it */ + for (tp = timef; tp < &timef[ntimef]; tp++) + if (tp->tid == timerID) + break; + if (tp == &timef[ntimef]) + return; + + /* bubble it out */ + for (++tp; tp < &timef[ntimef]; tp++) + tp[-1] = tp[0]; + + /* shrink list */ + timef = (TF *) realloc (timef, (--ntimef)*sizeof(CB)); +} + +/* add a new work procedure, fp, to be called with ud when nothing else to do. + * return unique id for use with rmWorkProc(). + */ +int +addWorkProc (WPF *fp, void *ud) +{ + WP *wp; + + for (wp = wproc; wp < &wproc[nwproc]; wp++) + if (!wp->in_use) + break; + + if (wp == &wproc[nwproc]) { + wproc = wproc ? (WP *) realloc (wproc, (nwproc+1)*sizeof(WP)) + : (WP *) malloc (sizeof(WP)); + wp = &wproc[nwproc++]; + } + + wp->in_use = 1; + wp->fp = fp; + wp->ud = ud; + nwpinuse++; + return (wp->wid = ++wid); +} + + +/* remove the work proc with the given id, as returned from addWorkProc(). + * silently ignore if id not found. + */ +void +rmWorkProc (int workID) +{ + WP *wp; + + for (wp = wproc; wp < &wproc[nwproc]; wp++) { + if (wp->in_use && wp->wid == workID) { + if (wp == &wproc[nwproc-1] && wp > wproc) + wproc = (WP *) realloc (wproc, (--nwproc)*sizeof(WP)); + else + wp->in_use = 0; + nwpinuse--; + break; + } + } +} + +/* run all registered work procedures */ +static void +runWorkProcs () +{ + WP *wp; + + for (wp = wproc; wp < &wproc[nwproc]; wp++) + if (wp->in_use) + (*wp->fp) (wp->ud); +} + +/* run all registered callbacks whose fd is listed in rfdp */ +static void +callCallbacks(fd_set *rfdp, int nready) +{ + CB *cp; + + for (cp = cback; nready > 0 && cp < &cback[ncback]; cp++) { + if (cp->in_use && FD_ISSET (cp->fd, rfdp)) { + (*cp->fp) (cp->fd, cp->ud); + nready--; + } + } +} + +/* run all timers that are ready to pop. timef[] is sorted such in decreasing + * order of time from epoch to run, ie, last entry runs soonest. + */ +static void +popTimers() +{ + struct timeval now; + int tgonow; + TF *tp; + + gettimeofday (&now, NULL); + tgonow = EPDT (&now); + for (tp = &timef[ntimef-1]; tp >= timef && tp->tgo <= tgonow; tp--) { + (*tp->fp) (tp->ud); + printf ("\a\n"); + ntimef--; + } +} + +/* check fd's from each active callback. + * if any ready, call their callbacks else call each registered work procedure. + */ +static void +oneLoop() +{ + struct timeval tv, *tvp; + fd_set rfd; + CB *cp; + int maxfd, ns; + + /* build list of file descriptors to check */ + FD_ZERO (&rfd); + maxfd = -1; + for (cp = cback; cp < &cback[ncback]; cp++) { + if (cp->in_use) { + FD_SET (cp->fd, &rfd); + if (cp->fd > maxfd) + maxfd = cp->fd; + } + } + + /* if there are work procs + * set delay = 0 + * else if there is at least one timer func + * set delay = time until soonest timer func expires + * else + * set delay = forever + */ + + if (nwpinuse > 0) { + tvp = &tv; + tvp->tv_sec = tvp->tv_usec = 0; + } else if (ntimef > 0) { + struct timeval now; + int late; + gettimeofday (&now, NULL); + late = timef[ntimef-1].tgo - EPDT (&now); + if (late < 0) + late = 0; + tvp = &tv; + tvp->tv_sec = late/1000; + tvp->tv_usec = 1000*(late%1000); + } else + tvp = NULL; + + /* check file descriptors, dispatch callbacks or workprocs as per info*/ + ns = select (maxfd+1, &rfd, NULL, NULL, tvp); + if (ns < 0) { + perror ("select"); + exit(1); + } + + /* dispatch */ + if (ns == 0) + runWorkProcs(); + else + callCallbacks(&rfd, ns); + if (ntimef > 0) + popTimers(); +} + +#if defined(MAIN_TEST) +/* make a small stand-alone test program. + */ + +#include <unistd.h> +#include <sys/time.h> + +int counter; +int mycid; +int mywid; +int mytid; + +char user_a = 'A'; +char user_b = 'B'; + +void +wp (void *ud) +{ + struct timeval tv; + char a = *(char *)ud; + + gettimeofday (&tv, NULL); + printf ("workproc: %c @ %ld.%03ld counter %d\n", a, (long)tv.tv_sec, + (long)tv.tv_usec/1000, counter); +} + +void +to (void *ud) +{ + printf ("timeout %d\n", (int)ud); +} + +void +stdinCB (int fd, void *ud) +{ + char b = *(char *)ud; + char c; + + if (read (fd, &c, 1) != 1) + exit(1); + + switch (c) { + case '+': counter++; break; + case '-': counter--; break; + + case 'W': mywid = addWorkProc (wp, &user_b); break; + case 'w': rmWorkProc (mywid); break; + + case 'c': rmCallback (mycid); break; + + case 't': rmTimer (mytid); break; + case '1': mytid = addTimer (1000, to, (void *)1); break; + case '2': mytid = addTimer (2000, to, (void *)2); break; + case '3': mytid = addTimer (3000, to, (void *)3); break; + case '4': mytid = addTimer (4000, to, (void *)4); break; + case '5': mytid = addTimer (5000, to, (void *)5); break; + default: return; /* silently absorb other chars like \n */ + } + + printf ("callback: %c counter is now %d\n", b, counter); +} + +int +main (int ac, char *av[]) +{ + cid = addCallback (0, stdinCB, &user_a); + eventLoop(); + exit(0); +} + +#endif + +/* For RCS Only -- Do Not Edit */ +static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile$ $Date$ $Revision$ $Name: $"}; |