3 Copyright (C
) 2003 Elwood C
. Downey
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
.
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
.
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
., 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
21 /* suite of functions to implement an event driven program.
23 * callbacks may be registered that are triggered when a file descriptor
24 * will not block when read;
26 * timers may be registered that will run no sooner than a specified delay from
27 * the moment they were registered;
29 * work procedures may be registered that are called when there is nothing
32 #define MAIN_TEST for a stand-alone test program.
39 #include <sys/types.h>
43 #include "eventloop.h"
45 /* info about one registered callback. */
47 int in_use
; /* flag to make this record is active */
48 int fd
; /* fd descriptor to watch for read */
49 void *ud
; /* user's data handle */
50 CBF
*fp
; /* callback function */
51 int cid
; /* unique id for this callback */
54 /* info about one registered timer function */
56 int tgo
; /* trigger time, ms from epoch */
57 void *ud
; /* user's data handle */
58 TCF
*fp
; /* timer function */
59 int tid
; /* unique id for this timer */
62 /* info about one registered work procedure. */
64 int in_use
; /* flag to make this record is active */
65 void *ud
; /* user's data handle */
66 WPF
*fp
; /* work proc function function */
67 int wid
; /* unique id for this work proc */
71 static CB
*cback
; /* malloced list of callbacks */
72 static int ncback
; /* n entries in cback[] */
73 static int cid
; /* source of callback ids */
75 static TF
*timef
; /* malloced list of timer functions */
76 static int ntimef
; /* n entries in ntimef[] */
77 static struct timeval epoch
; /* arbitrary t0 */
78 static int tid
; /* source of timer ids */
79 #define EPDT(tp) /* ms from epoch to timeval *tp */ \
80 (((tp)->tv_sec-epoch.tv_sec)*1000 + ((tp)->tv_usec-epoch.tv_usec)/1000)
82 static WP
*wproc
; /* malloced list of work procedures */
83 static int nwproc
; /* n entries in wproc[] */
84 static int nwpinuse
; /* n entries in wproc[] marked in-use */
85 static int wid
; /* source of worproc ids */
87 static void runWorkProcs (void);
88 static void callCallbacks(fd_set
*rfdp
, int nready
);
89 static void popTimers();
90 static void oneLoop(void);
92 /* inf loop to dispatch callbacks, work procs and timers as necessary.
98 /* init epoch to now */
99 gettimeofday (&epoch
, NULL
);
101 /* run loop forever */
106 /* register a new callback, fp, to be called with ud as arg when fd is ready.
107 * return a unique callback id for use with rmCallback().
110 addCallback (int fd
, CBF
*fp
, void *ud
)
114 for (cp
= cback
; cp
< &cback
[ncback
]; cp
++)
118 if (cp
== &cback
[ncback
]) {
119 cback
= cback
? (CB
*) realloc (cback
, (ncback
+1)*sizeof(CB
))
120 : (CB
*) malloc (sizeof(CB
));
121 cp
= &cback
[ncback
++];
128 return (cp
->cid
= ++cid
);
131 /* remove the callback with the given id, as returned from addCallback().
132 * silently ignore if id not found.
135 rmCallback (int callbackid
)
139 for (cp
= cback
; cp
< &cback
[ncback
]; cp
++) {
140 if (cp
->in_use
&& cp
->cid
== callbackid
) {
147 /* register a new timer function, fp, to be called with ud as arg after ms
148 * milliseconds. add to list in order of decreasing time from epoch, ie,
149 * last entry runs soonest. return id for use with rmTimer().
152 addTimer (int ms
, TCF
*fp
, void *ud
)
157 gettimeofday (&t
, NULL
);
159 timef
= timef
? (TF
*) realloc (timef
, (ntimef
+1)*sizeof(TF
))
160 : (TF
*) malloc (sizeof(TF
));
161 tp
= &timef
[ntimef
++];
165 tp
->tgo
= EPDT(&t
) + ms
;
167 for ( ; tp
> timef
&& tp
[0].tgo
> tp
[-1].tgo
; tp
--) {
173 return (tp
->tid
= ++tid
);
176 /* remove the timer with the given id, as returned from addTimer().
177 * silently ignore if id not found.
180 rmTimer (int timerID
)
185 for (tp
= timef
; tp
< &timef
[ntimef
]; tp
++)
186 if (tp
->tid
== timerID
)
188 if (tp
== &timef
[ntimef
])
192 for (++tp
; tp
< &timef
[ntimef
]; tp
++)
196 timef
= (TF
*) realloc (timef
, (--ntimef
)*sizeof(CB
));
199 /* add a new work procedure, fp, to be called with ud when nothing else to do.
200 * return unique id for use with rmWorkProc().
203 addWorkProc (WPF
*fp
, void *ud
)
207 for (wp
= wproc
; wp
< &wproc
[nwproc
]; wp
++)
211 if (wp
== &wproc
[nwproc
]) {
212 wproc
= wproc
? (WP
*) realloc (wproc
, (nwproc
+1)*sizeof(WP
))
213 : (WP
*) malloc (sizeof(WP
));
214 wp
= &wproc
[nwproc
++];
221 return (wp
->wid
= ++wid
);
225 /* remove the work proc with the given id, as returned from addWorkProc().
226 * silently ignore if id not found.
229 rmWorkProc (int workID
)
233 for (wp
= wproc
; wp
< &wproc
[nwproc
]; wp
++) {
234 if (wp
->in_use
&& wp
->wid
== workID
) {
235 if (wp
== &wproc
[nwproc
-1] && wp
> wproc
)
236 wproc
= (WP
*) realloc (wproc
, (--nwproc
)*sizeof(WP
));
245 /* run all registered work procedures */
251 for (wp
= wproc
; wp
< &wproc
[nwproc
]; wp
++)
256 /* run all registered callbacks whose fd is listed in rfdp */
258 callCallbacks(fd_set
*rfdp
, int nready
)
262 for (cp
= cback
; nready
> 0 && cp
< &cback
[ncback
]; cp
++) {
263 if (cp
->in_use
&& FD_ISSET (cp
->fd
, rfdp
)) {
264 (*cp
->fp
) (cp
->fd
, cp
->ud
);
270 /* run all timers that are ready to pop. timef[] is sorted such in decreasing
271 * order of time from epoch to run, ie, last entry runs soonest.
280 gettimeofday (&now
, NULL
);
281 tgonow
= EPDT (&now
);
282 for (tp
= &timef
[ntimef
-1]; tp
>= timef
&& tp
->tgo
<= tgonow
; tp
--) {
289 /* check fd's from each active callback.
290 * if any ready, call their callbacks else call each registered work procedure.
295 struct timeval tv
, *tvp
;
300 /* build list of file descriptors to check */
303 for (cp
= cback
; cp
< &cback
[ncback
]; cp
++) {
305 FD_SET (cp
->fd
, &rfd
);
311 /* if there are work procs
313 * else if there is at least one timer func
314 * set delay = time until soonest timer func expires
316 * set delay = forever
321 tvp
->tv_sec
= tvp
->tv_usec
= 0;
322 } else if (ntimef
> 0) {
325 gettimeofday (&now
, NULL
);
326 late
= timef
[ntimef
-1].tgo
- EPDT (&now
);
330 tvp
->tv_sec
= late
/1000;
331 tvp
->tv_usec
= 1000*(late
%1000);
335 /* check file descriptors, dispatch callbacks or workprocs as per info*/
336 ns
= select (maxfd
+1, &rfd
, NULL
, NULL
, tvp
);
346 callCallbacks(&rfd
, ns
);
351 #if defined(MAIN_TEST)
352 /* make a small stand-alone test program.
356 #include <sys/time.h>
370 char a
= *(char *)ud
;
372 gettimeofday (&tv
, NULL
);
373 printf ("workproc: %c @ %ld.%03ld counter %d\n", a
, (long)tv
.tv_sec
,
374 (long)tv
.tv_usec
/1000, counter
);
380 printf ("timeout %d\n", (int)ud
);
384 stdinCB (int fd
, void *ud
)
386 char b
= *(char *)ud
;
389 if (read (fd
, &c
, 1) != 1)
393 case '+': counter
++; break;
394 case '-': counter
--; break;
396 case 'W': mywid
= addWorkProc (wp
, &user_b
); break;
397 case 'w': rmWorkProc (mywid
); break;
399 case 'c': rmCallback (mycid
); break;
401 case 't': rmTimer (mytid
); break;
402 case '1': mytid
= addTimer (1000, to
, (void *)1); break;
403 case '2': mytid
= addTimer (2000, to
, (void *)2); break;
404 case '3': mytid
= addTimer (3000, to
, (void *)3); break;
405 case '4': mytid
= addTimer (4000, to
, (void *)4); break;
406 case '5': mytid
= addTimer (5000, to
, (void *)5); break;
407 default: return; /* silently absorb other chars like \n */
410 printf ("callback: %c counter is now %d\n", b
, counter
);
414 main (int ac
, char *av
[])
416 cid
= addCallback (0, stdinCB
, &user_a
);
423 /* For RCS Only -- Do Not Edit */
424 static char *rcsid
[2] = {(char *)rcsid
, "@(#) $RCSfile$ $Date$ $Revision$ $Name: $"};