source: BAORadio/libindi/v1.0.1/eventloop.c@ 674

Last change on this file since 674 was 490, checked in by campagne, 15 years ago

import libindi (JEC)

File size: 12.2 KB
Line 
1#if 0
2 INDI
3 Copyright (C) 2003 Elwood C. Downey
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19#endif
20
21/* suite of functions to implement an event driven program.
22 *
23 * callbacks may be registered that are triggered when a file descriptor
24 * will not block when read;
25 *
26 * timers may be registered that will run no sooner than a specified delay from
27 * the moment they were registered;
28 *
29 * work procedures may be registered that are called when there is nothing
30 * else to do;
31 *
32 #define MAIN_TEST for a stand-alone test program.
33 */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <math.h>
38#include <string.h>
39#include <time.h>
40#include <sys/types.h>
41#include <sys/time.h>
42#include <unistd.h>
43
44#include "eventloop.h"
45
46/* info about one registered callback.
47 * the malloced array cback is never shrunk, entries are reused. new id's are
48 * the index of first unused slot in array (and thus reused like unix' open(2)).
49 */
50typedef struct {
51 int in_use; /* flag to mark this record is active */
52 int fd; /* fd descriptor to watch for read */
53 void *ud; /* user's data handle */
54 CBF *fp; /* callback function */
55} CB;
56static CB *cback; /* malloced list of callbacks */
57static int ncback; /* n entries in cback[] */
58static int ncbinuse; /* n entries in cback[] marked in_use */
59static int lastcb; /* cback index of last cb called */
60
61/* info about one registered timer function.
62 * the entries are kept sorted by decreasing time from epoch, ie,
63 * the next entry to fire is at the end of the array.
64 */
65typedef struct {
66 double tgo; /* trigger time, ms from epoch */
67 void *ud; /* user's data handle */
68 TCF *fp; /* timer function */
69 int tid; /* unique id for this timer */
70} TF;
71static TF *timef; /* malloced list of timer functions */
72static int ntimef; /* n entries in ntimef[] */
73static int tid; /* source of unique timer ids */
74#define EPOCHDT(tp) /* ms from epoch to timeval *tp */ \
75 (((tp)->tv_usec)/1000.0 + ((tp)->tv_sec)*1000.0)
76
77/* info about one registered work procedure.
78 * the malloced array wproc is never shrunk, entries are reused. new id's are
79 * the index of first unused slot in array (and thus reused like unix' open(2)).
80 */
81typedef struct {
82 int in_use; /* flag to mark this record is active */
83 void *ud; /* user's data handle */
84 WPF *fp; /* work proc function function */
85} WP;
86static WP *wproc; /* malloced list of work procedures */
87static int nwproc; /* n entries in wproc[] */
88static int nwpinuse; /* n entries in wproc[] marked in-use */
89static int lastwp; /* wproc index of last workproc called*/
90
91static void runWorkProc (void);
92static void callCallback(fd_set *rfdp);
93static void checkTimer();
94static void oneLoop(void);
95static void deferTO (void *p);
96
97/* inf loop to dispatch callbacks, work procs and timers as necessary.
98 * never returns.
99 */
100void
101eventLoop()
102{
103 /* run loop forever */
104 while (1)
105 oneLoop();
106}
107
108/* allow other timers/callbacks/workprocs to run until time out in maxms
109 * or *flagp becomes non-0. wait forever if maxms is 0.
110 * return 0 if flag did flip, else -1 if never changed and we timed out.
111 * the expected usage for this is for the caller to arrange for a T/C/W to set
112 * a flag, then give caller an in-line way to wait for the flag to change.
113 */
114int
115deferLoop (int maxms, int *flagp)
116{
117 int toflag = 0;
118 int totid = maxms ? addTimer (maxms, deferTO, &toflag) : 0;
119
120 while (!*flagp) {
121 oneLoop();
122 if (toflag)
123 return (-1); /* totid already dead */
124 }
125
126 if (totid)
127 rmTimer (totid);
128 return (0);
129}
130
131/* allow other timers/callbacks/workprocs to run until time out in maxms
132 * or *flagp becomes 0. wait forever if maxms is 0.
133 * return 0 if flag did flip, else -1 if never changed and we timed out.
134 * the expected usage for this is for the caller to arrange for a T/C/W to set
135 * a flag, then give caller an in-line way to wait for the flag to change.
136 */
137int
138deferLoop0 (int maxms, int *flagp)
139{
140 int toflag = 0;
141 int totid = maxms ? addTimer (maxms, deferTO, &toflag) : 0;
142
143 while (*flagp) {
144 oneLoop();
145 if (toflag)
146 return (-1); /* totid already dead */
147 }
148
149 if (totid)
150 rmTimer (totid);
151 return (0);
152}
153
154/* register a new callback, fp, to be called with ud as arg when fd is ready.
155 * return a unique callback id for use with rmCallback().
156 */
157int
158addCallback (int fd, CBF *fp, void *ud)
159{
160 CB *cp;
161
162 /* reuse first unused slot or grow */
163 for (cp = cback; cp < &cback[ncback]; cp++)
164 if (!cp->in_use)
165 break;
166 if (cp == &cback[ncback]) {
167 cback = cback ? (CB *) realloc (cback, (ncback+1)*sizeof(CB))
168 : (CB *) malloc (sizeof(CB));
169 cp = &cback[ncback++];
170 }
171
172 /* init new entry */
173 cp->in_use = 1;
174 cp->fp = fp;
175 cp->ud = ud;
176 cp->fd = fd;
177 ncbinuse++;
178
179 /* id is index into array */
180 return (cp - cback);
181}
182
183/* remove the callback with the given id, as returned from addCallback().
184 * silently ignore if id not valid.
185 */
186void
187rmCallback (int cid)
188{
189 CB *cp;
190
191 /* validate id */
192 if (cid < 0 || cid >= ncback)
193 return;
194 cp = &cback[cid];
195 if (!cp->in_use)
196 return;
197
198 /* mark for reuse */
199 cp->in_use = 0;
200 ncbinuse--;
201}
202
203/* register a new timer function, fp, to be called with ud as arg after ms
204 * milliseconds. add to list in order of decreasing time from epoch, ie,
205 * last entry runs soonest. return id for use with rmTimer().
206 */
207int
208addTimer (int ms, TCF *fp, void *ud)
209{
210 struct timeval t;
211 TF *tp;
212
213 /* get time now */
214 gettimeofday (&t, NULL);
215
216 /* add one entry */
217 timef = timef ? (TF *) realloc (timef, (ntimef+1)*sizeof(TF))
218 : (TF *) malloc (sizeof(TF));
219 tp = &timef[ntimef++];
220
221 /* init new entry */
222 tp->ud = ud;
223 tp->fp = fp;
224 tp->tgo = EPOCHDT(&t) + ms;
225
226 /* insert maintaining sort */
227 for ( ; tp > timef && tp[0].tgo > tp[-1].tgo; tp--) {
228 TF tmptf = tp[-1];
229 tp[-1] = tp[0];
230 tp[0] = tmptf;
231 }
232
233 /* store and return new unique id */
234 return (tp->tid = ++tid);
235}
236
237/* remove the timer with the given id, as returned from addTimer().
238 * silently ignore if id not found.
239 */
240void
241rmTimer (int timer_id)
242{
243 TF *tp;
244
245 /* find it */
246 for (tp = timef; tp < &timef[ntimef]; tp++)
247 if (tp->tid == timer_id)
248 break;
249 if (tp == &timef[ntimef])
250 return;
251
252 /* bubble it out */
253 for (++tp; tp < &timef[ntimef]; tp++)
254 tp[-1] = tp[0];
255
256 /* shrink list */
257 timef = (TF *) realloc (timef, (--ntimef)*sizeof(TF));
258}
259
260/* add a new work procedure, fp, to be called with ud when nothing else to do.
261 * return unique id for use with rmWorkProc().
262 */
263int
264addWorkProc (WPF *fp, void *ud)
265{
266 WP *wp;
267
268 /* reuse first unused slot or grow */
269 for (wp = wproc; wp < &wproc[nwproc]; wp++)
270 if (!wp->in_use)
271 break;
272 if (wp == &wproc[nwproc]) {
273 wproc = wproc ? (WP *) realloc (wproc, (nwproc+1)*sizeof(WP))
274 : (WP *) malloc (sizeof(WP));
275 wp = &wproc[nwproc++];
276 }
277
278 /* init new entry */
279 wp->in_use = 1;
280 wp->fp = fp;
281 wp->ud = ud;
282 nwpinuse++;
283
284 /* id is index into array */
285 return (wp - wproc);
286}
287
288
289/* remove the work proc with the given id, as returned from addWorkProc().
290 * silently ignore if id not found.
291 */
292void
293rmWorkProc (int wid)
294{
295 WP *wp;
296
297 /* validate id */
298 if (wid < 0 || wid >= nwproc)
299 return;
300 wp = &wproc[wid];
301 if (!wp->in_use)
302 return;
303
304 /* mark for reuse */
305 wp->in_use = 0;
306 nwpinuse--;
307}
308
309/* run next work procedure */
310static void
311runWorkProc ()
312{
313 WP *wp;
314
315 /* skip if list is empty */
316 if (!nwpinuse)
317 return;
318
319 /* find next */
320 do {
321 lastwp = (lastwp+1) % nwproc;
322 wp = &wproc[lastwp];
323 } while (!wp->in_use);
324
325 /* run */
326 (*wp->fp) (wp->ud);
327}
328
329/* run next callback whose fd is listed as ready to go in rfdp */
330static void
331callCallback(fd_set *rfdp)
332{
333 CB *cp;
334
335 /* skip if list is empty */
336 if (!ncbinuse)
337 return;
338
339 /* find next */
340 do {
341 lastcb = (lastcb+1) % ncback;
342 cp = &cback[lastcb];
343 } while (!cp->in_use || !FD_ISSET (cp->fd, rfdp));
344
345 /* run */
346 (*cp->fp) (cp->fd, cp->ud);
347}
348
349/* run the next timer callback whose time has come, if any. all we have to do
350 * is is check the last entry in timef[] because it is sorted in decreasing
351 * order of time from epoch to run, ie, last entry runs soonest.
352 */
353static void
354checkTimer()
355{
356 struct timeval now;
357 double tgonow;
358 TF *tp;
359
360 /* skip if list is empty */
361 if (!ntimef)
362 return;
363
364 gettimeofday (&now, NULL);
365 tgonow = EPOCHDT (&now);
366 tp = &timef[ntimef-1];
367 if (tp->tgo <= tgonow) {
368 ntimef--; /* pop then call */
369 (*tp->fp) (tp->ud);
370 }
371}
372
373/* check fd's from each active callback.
374 * if any ready, call their callbacks else call each registered work procedure.
375 */
376static void
377oneLoop()
378{
379 struct timeval tv, *tvp;
380 fd_set rfd;
381 CB *cp;
382 int maxfd, ns;
383
384 /* build list of callback file descriptors to check */
385 FD_ZERO (&rfd);
386 maxfd = -1;
387 for (cp = cback; cp < &cback[ncback]; cp++) {
388 if (cp->in_use) {
389 FD_SET (cp->fd, &rfd);
390 if (cp->fd > maxfd)
391 maxfd = cp->fd;
392 }
393 }
394
395 /* determine timeout:
396 * if there are work procs
397 * set delay = 0
398 * else if there is at least one timer func
399 * set delay = time until soonest timer func expires
400 * else
401 * set delay = forever
402 */
403 if (nwpinuse > 0) {
404 tvp = &tv;
405 tvp->tv_sec = tvp->tv_usec = 0;
406 } else if (ntimef > 0) {
407 struct timeval now;
408 double late;
409 gettimeofday (&now, NULL);
410 late = timef[ntimef-1].tgo - EPOCHDT (&now); /* ms late */
411 if (late < 0)
412 late = 0;
413 late /= 1000.0; /* secs late */
414 tvp = &tv;
415 tvp->tv_sec = (long)floor(late);
416 tvp->tv_usec = (long)floor((late - tvp->tv_sec)*1000000.0);
417 } else
418 tvp = NULL;
419
420 /* check file descriptors, timeout depending on pending work */
421 ns = select (maxfd+1, &rfd, NULL, NULL, tvp);
422 if (ns < 0) {
423 perror ("select");
424 exit(1);
425 }
426
427 /* dispatch */
428 checkTimer();
429 if (ns == 0)
430 runWorkProc();
431 else
432 callCallback(&rfd);
433}
434
435/* timer callback used to implement deferLoop().
436 * arg is pointer to int which we set to 1
437 */
438static void
439deferTO (void *p)
440{
441 *(int*)p = 1;
442}
443
444#if defined(MAIN_TEST)
445/* make a small stand-alone test program.
446 */
447
448#include <unistd.h>
449#include <sys/time.h>
450
451int mycid;
452int mywid;
453int mytid;
454
455int user_a;
456int user_b;
457int counter;
458
459void
460wp (void *ud)
461{
462 struct timeval tv;
463
464 gettimeofday (&tv, NULL);
465 printf ("workproc @ %ld.%03ld %d %d\n",
466 (long)tv.tv_sec, (long)tv.tv_usec/1000, counter, ++(*(int*)ud));
467}
468
469void
470to (void *ud)
471{
472 printf ("timeout %d\n", (int)ud);
473}
474
475void
476stdinCB (int fd, void *ud)
477{
478 char c;
479
480 if (read (fd, &c, 1) != 1) {
481 perror ("read");
482 exit(1);
483 }
484
485 switch (c) {
486 case '+': counter++; break;
487 case '-': counter--; break;
488
489 case 'W': mywid = addWorkProc (wp, &user_b); break;
490 case 'w': rmWorkProc (mywid); break;
491
492 case 'c': rmCallback (mycid); break;
493
494 case 't': rmTimer (mytid); break;
495 case '1': mytid = addTimer (1000, to, (void *)1); break;
496 case '2': mytid = addTimer (2000, to, (void *)2); break;
497 case '3': mytid = addTimer (3000, to, (void *)3); break;
498 case '4': mytid = addTimer (4000, to, (void *)4); break;
499 case '5': mytid = addTimer (5000, to, (void *)5); break;
500 default: return; /* silently absorb other chars like \n */
501 }
502
503 printf ("callback: %d\n", ++(*(int*)ud));
504}
505
506int
507main (int ac, char *av[])
508{
509 (void) addCallback (0, stdinCB, &user_a);
510 eventLoop();
511 exit(0);
512}
513
514#endif
515
Note: See TracBrowser for help on using the repository browser.