1 /***********************************************************************
5 * Abstraction of select call into "event-handling" to make programming
8 * Copyright (C) 2001 Roaring Penguin Software Inc.
10 * This program may be distributed according to the terms of the GNU
11 * General Public License, version 2 or (at your option) any later version.
15 ***********************************************************************/
17 static char const RCSID
[] =
18 "$Id: event.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $";
24 static void DestroySelector(EventSelector
*es
);
25 static void DestroyHandler(EventHandler
*eh
);
26 static void DoPendingChanges(EventSelector
*es
);
28 /**********************************************************************
29 * %FUNCTION: Event_CreateSelector
33 * A newly-allocated EventSelector, or NULL if out of memory.
35 * Creates a new EventSelector.
36 ***********************************************************************/
38 Event_CreateSelector(void)
40 EventSelector
*es
= malloc(sizeof(EventSelector
));
44 es
->destroyPending
= 0;
46 EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es
));
50 /**********************************************************************
51 * %FUNCTION: Event_DestroySelector
53 * es -- EventSelector to destroy
57 * Destroys an EventSelector. Destruction may be delayed if we
58 * are in the HandleEvent function.
59 ***********************************************************************/
61 Event_DestroySelector(EventSelector
*es
)
64 es
->destroyPending
= 1;
71 /**********************************************************************
72 * %FUNCTION: Event_HandleEvent
76 * 0 if OK, non-zero on error. errno is set appropriately.
78 * Handles a single event (uses select() to wait for an event.)
79 ***********************************************************************/
81 Event_HandleEvent(EventSelector
*es
)
83 fd_set readfds
, writefds
;
87 struct timeval abs_timeout
, now
;
88 struct timeval timeout
;
94 int foundTimeoutEvent
= 0;
95 int foundReadEvent
= 0;
96 int foundWriteEvent
= 0;
100 EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es
));
102 /* Build the select sets */
107 for (eh
=es
->handlers
; eh
; eh
=eh
->next
) {
108 if (eh
->flags
& EVENT_FLAG_DELETED
) continue;
109 if (eh
->flags
& EVENT_FLAG_READABLE
) {
111 FD_SET(eh
->fd
, &readfds
);
112 if (eh
->fd
> maxfd
) maxfd
= eh
->fd
;
114 if (eh
->flags
& EVENT_FLAG_WRITEABLE
) {
116 FD_SET(eh
->fd
, &writefds
);
117 if (eh
->fd
> maxfd
) maxfd
= eh
->fd
;
119 if (eh
->flags
& EVENT_TIMER_BITS
) {
120 if (!foundTimeoutEvent
) {
121 abs_timeout
= eh
->tmout
;
122 foundTimeoutEvent
= 1;
124 if (eh
->tmout
.tv_sec
< abs_timeout
.tv_sec
||
125 (eh
->tmout
.tv_sec
== abs_timeout
.tv_sec
&&
126 eh
->tmout
.tv_usec
< abs_timeout
.tv_usec
)) {
127 abs_timeout
= eh
->tmout
;
132 if (foundReadEvent
) {
137 if (foundWriteEvent
) {
143 if (foundTimeoutEvent
) {
144 gettimeofday(&now
, NULL
);
145 /* Convert absolute timeout to relative timeout for select */
146 timeout
.tv_usec
= abs_timeout
.tv_usec
- now
.tv_usec
;
147 timeout
.tv_sec
= abs_timeout
.tv_sec
- now
.tv_sec
;
148 if (timeout
.tv_usec
< 0) {
149 timeout
.tv_usec
+= 1000000;
152 if (timeout
.tv_sec
< 0 ||
153 (timeout
.tv_sec
== 0 && timeout
.tv_usec
< 0)) {
162 if (foundReadEvent
|| foundWriteEvent
|| foundTimeoutEvent
) {
164 r
= select(maxfd
+1, rd
, wr
, NULL
, tm
);
166 if (errno
== EINTR
) continue;
172 if (foundTimeoutEvent
) gettimeofday(&now
, NULL
);
178 for (eh
=es
->handlers
; eh
; eh
=eh
->next
) {
180 /* Pending delete for this handler? Ignore it */
181 if (eh
->flags
& EVENT_FLAG_DELETED
) continue;
184 if ((eh
->flags
& EVENT_FLAG_READABLE
) &&
185 FD_ISSET(eh
->fd
, &readfds
)) {
186 flags
|= EVENT_FLAG_READABLE
;
188 if ((eh
->flags
& EVENT_FLAG_WRITEABLE
) &&
189 FD_ISSET(eh
->fd
, &writefds
)) {
190 flags
|= EVENT_FLAG_WRITEABLE
;
192 if (eh
->flags
& EVENT_TIMER_BITS
) {
193 pastDue
= (eh
->tmout
.tv_sec
< now
.tv_sec
||
194 (eh
->tmout
.tv_sec
== now
.tv_sec
&&
195 eh
->tmout
.tv_usec
<= now
.tv_usec
));
197 flags
|= EVENT_TIMER_BITS
;
198 if (eh
->flags
& EVENT_FLAG_TIMER
) {
199 /* Timer events are only called once */
201 eh
->flags
|= EVENT_FLAG_DELETED
;
207 EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh
, flags
));
208 eh
->fn(es
, eh
->fd
, flags
, eh
->data
);
209 EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh
, flags
));
216 if (!es
->nestLevel
&& es
->opsPending
) {
217 DoPendingChanges(es
);
223 /**********************************************************************
224 * %FUNCTION: Event_AddHandler
226 * es -- event selector
227 * fd -- file descriptor to watch
228 * flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
229 * fn -- callback function to call when event is triggered
230 * data -- extra data to pass to callback function
232 * A newly-allocated EventHandler, or NULL.
233 ***********************************************************************/
235 Event_AddHandler(EventSelector
*es
,
238 EventCallbackFunc fn
,
243 /* Specifically disable timer and deleted flags */
244 flags
&= (~(EVENT_TIMER_BITS
| EVENT_FLAG_DELETED
));
246 /* Bad file descriptor */
252 eh
= malloc(sizeof(EventHandler
));
253 if (!eh
) return NULL
;
256 eh
->tmout
.tv_usec
= 0;
257 eh
->tmout
.tv_sec
= 0;
261 /* Add immediately. This is safe even if we are in a handler. */
262 eh
->next
= es
->handlers
;
265 EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es
, fd
, flags
, eh
));
269 /**********************************************************************
270 * %FUNCTION: Event_AddHandlerWithTimeout
272 * es -- event selector
273 * fd -- file descriptor to watch
274 * flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
275 * t -- Timeout after which to call handler, even if not readable/writable.
276 * If t.tv_sec < 0, calls normal Event_AddHandler with no timeout.
277 * fn -- callback function to call when event is triggered
278 * data -- extra data to pass to callback function
280 * A newly-allocated EventHandler, or NULL.
281 ***********************************************************************/
283 Event_AddHandlerWithTimeout(EventSelector
*es
,
287 EventCallbackFunc fn
,
293 /* If timeout is negative, just do normal non-timing-out event */
294 if (t
.tv_sec
< 0 || t
.tv_usec
< 0) {
295 return Event_AddHandler(es
, fd
, flags
, fn
, data
);
298 /* Specifically disable timer and deleted flags */
299 flags
&= (~(EVENT_FLAG_TIMER
| EVENT_FLAG_DELETED
));
300 flags
|= EVENT_FLAG_TIMEOUT
;
302 /* Bad file descriptor? */
309 if (t
.tv_usec
>= 1000000) {
314 eh
= malloc(sizeof(EventHandler
));
315 if (!eh
) return NULL
;
317 /* Convert time interval to absolute time */
318 gettimeofday(&now
, NULL
);
320 t
.tv_sec
+= now
.tv_sec
;
321 t
.tv_usec
+= now
.tv_usec
;
322 if (t
.tv_usec
>= 1000000) {
323 t
.tv_usec
-= 1000000;
333 /* Add immediately. This is safe even if we are in a handler. */
334 eh
->next
= es
->handlers
;
337 EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es
, fd
, flags
, t
.tv_sec
, t
.tv_usec
, eh
));
342 /**********************************************************************
343 * %FUNCTION: Event_AddTimerHandler
345 * es -- event selector
346 * t -- time interval after which to trigger event
347 * fn -- callback function to call when event is triggered
348 * data -- extra data to pass to callback function
350 * A newly-allocated EventHandler, or NULL.
351 ***********************************************************************/
353 Event_AddTimerHandler(EventSelector
*es
,
355 EventCallbackFunc fn
,
361 /* Check time interval for validity */
362 if (t
.tv_sec
< 0 || t
.tv_usec
< 0 || t
.tv_usec
>= 1000000) {
367 eh
= malloc(sizeof(EventHandler
));
368 if (!eh
) return NULL
;
370 /* Convert time interval to absolute time */
371 gettimeofday(&now
, NULL
);
373 t
.tv_sec
+= now
.tv_sec
;
374 t
.tv_usec
+= now
.tv_usec
;
375 if (t
.tv_usec
>= 1000000) {
376 t
.tv_usec
-= 1000000;
381 eh
->flags
= EVENT_FLAG_TIMER
;
386 /* Add immediately. This is safe even if we are in a handler. */
387 eh
->next
= es
->handlers
;
390 EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es
, t
.tv_sec
,t
.tv_usec
, eh
));
394 /**********************************************************************
395 * %FUNCTION: Event_DelHandler
397 * es -- event selector
398 * eh -- event handler
400 * 0 if OK, non-zero if there is an error
402 * Deletes the event handler eh
403 ***********************************************************************/
405 Event_DelHandler(EventSelector
*es
,
408 /* Scan the handlers list */
409 EventHandler
*cur
, *prev
;
410 EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es
, eh
));
411 for (cur
=es
->handlers
, prev
=NULL
; cur
; prev
=cur
, cur
=cur
->next
) {
414 eh
->flags
|= EVENT_FLAG_DELETED
;
418 if (prev
) prev
->next
= cur
->next
;
419 else es
->handlers
= cur
->next
;
427 /* Handler not found */
431 /**********************************************************************
432 * %FUNCTION: DestroySelector
434 * es -- an event selector
438 * Destroys selector and all associated handles.
439 ***********************************************************************/
441 DestroySelector(EventSelector
*es
)
443 EventHandler
*cur
, *next
;
444 for (cur
=es
->handlers
; cur
; cur
=next
) {
452 /**********************************************************************
453 * %FUNCTION: DestroyHandler
455 * eh -- an event handler
460 ***********************************************************************/
462 DestroyHandler(EventHandler
*eh
)
464 EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh
));
468 /**********************************************************************
469 * %FUNCTION: DoPendingChanges
471 * es -- an event selector
475 * Makes all pending insertions and deletions happen.
476 ***********************************************************************/
478 DoPendingChanges(EventSelector
*es
)
480 EventHandler
*cur
, *prev
, *next
;
484 /* If selector is to be deleted, do it and skip everything else */
485 if (es
->destroyPending
) {
494 if (!(cur
->flags
& EVENT_FLAG_DELETED
)) {
500 /* Unlink from list */
502 prev
->next
= cur
->next
;
504 es
->handlers
= cur
->next
;
512 /**********************************************************************
513 * %FUNCTION: Event_GetCallback
515 * eh -- the event handler
517 * The callback function
518 ***********************************************************************/
520 Event_GetCallback(EventHandler
*eh
)
525 /**********************************************************************
526 * %FUNCTION: Event_GetData
528 * eh -- the event handler
531 ***********************************************************************/
533 Event_GetData(EventHandler
*eh
)
538 /**********************************************************************
539 * %FUNCTION: Event_SetCallbackAndData
541 * eh -- the event handler
542 * fn -- new callback function
543 * data -- new data value
547 * Sets the callback function and data fields.
548 ***********************************************************************/
550 Event_SetCallbackAndData(EventHandler
*eh
,
551 EventCallbackFunc fn
,
561 FILE *Event_DebugFP
= NULL
;
562 /**********************************************************************
563 * %FUNCTION: Event_DebugMsg
565 * fmt, ... -- format string
569 * Writes a debug message to the debug file.
570 ***********************************************************************/
572 Event_DebugMsg(char const *fmt
, ...)
577 if (!Event_DebugFP
) return;
579 gettimeofday(&now
, NULL
);
581 fprintf(Event_DebugFP
, "%03d.%03d ", (int) now
.tv_sec
% 1000,
582 (int) now
.tv_usec
/ 1000);
585 vfprintf(Event_DebugFP
, fmt
, ap
);
587 fflush(Event_DebugFP
);
592 /**********************************************************************
593 * %FUNCTION: Event_EnableDebugging
595 * fname -- name of file to log debug messages to
597 * 1 if debugging was enabled; 0 otherwise.
598 ***********************************************************************/
600 Event_EnableDebugging(char const *fname
)
605 Event_DebugFP
= fopen(fname
, "w");
606 return (Event_DebugFP
!= NULL
);
610 /**********************************************************************
611 * %FUNCTION: Event_ChangeTimeout
618 * Changes timeout of event handler to be "t" seconds in the future.
619 ***********************************************************************/
621 Event_ChangeTimeout(EventHandler
*h
, struct timeval t
)
625 /* Check time interval for validity */
626 if (t
.tv_sec
< 0 || t
.tv_usec
< 0 || t
.tv_usec
>= 1000000) {
629 /* Convert time interval to absolute time */
630 gettimeofday(&now
, NULL
);
632 t
.tv_sec
+= now
.tv_sec
;
633 t
.tv_usec
+= now
.tv_usec
;
634 if (t
.tv_usec
>= 1000000) {
635 t
.tv_usec
-= 1000000;