summaryrefslogtreecommitdiff
path: root/src/libevent/event.c (plain)
blob: d6514cbc460c5326c3ae1000828309300c0f3630
1/***********************************************************************
2*
3* event.c
4*
5* Abstraction of select call into "event-handling" to make programming
6* easier.
7*
8* Copyright (C) 2001 Roaring Penguin Software Inc.
9*
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.
12*
13* LIC: GPL
14*
15***********************************************************************/
16
17static char const RCSID[] =
18"$Id$";
19
20#include "event.h"
21#include <stdlib.h>
22#include <errno.h>
23
24static void DestroySelector(EventSelector *es);
25static void DestroyHandler(EventHandler *eh);
26static void DoPendingChanges(EventSelector *es);
27
28/**********************************************************************
29* %FUNCTION: Event_CreateSelector
30* %ARGUMENTS:
31* None
32* %RETURNS:
33* A newly-allocated EventSelector, or NULL if out of memory.
34* %DESCRIPTION:
35* Creates a new EventSelector.
36***********************************************************************/
37EventSelector *
38Event_CreateSelector(void)
39{
40 EventSelector *es = malloc(sizeof(EventSelector));
41 if (!es) return NULL;
42 es->handlers = NULL;
43 es->nestLevel = 0;
44 es->destroyPending = 0;
45 es->opsPending = 0;
46 EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es));
47 return es;
48}
49
50/**********************************************************************
51* %FUNCTION: Event_DestroySelector
52* %ARGUMENTS:
53* es -- EventSelector to destroy
54* %RETURNS:
55* Nothing
56* %DESCRIPTION:
57* Destroys an EventSelector. Destruction may be delayed if we
58* are in the HandleEvent function.
59***********************************************************************/
60void
61Event_DestroySelector(EventSelector *es)
62{
63 if (es->nestLevel) {
64 es->destroyPending = 1;
65 es->opsPending = 1;
66 return;
67 }
68 DestroySelector(es);
69}
70
71/**********************************************************************
72* %FUNCTION: Event_HandleEvent
73* %ARGUMENTS:
74* es -- EventSelector
75* %RETURNS:
76* 0 if OK, non-zero on error. errno is set appropriately.
77* %DESCRIPTION:
78* Handles a single event (uses select() to wait for an event.)
79***********************************************************************/
80int
81Event_HandleEvent(EventSelector *es)
82{
83 fd_set readfds, writefds;
84 fd_set *rd, *wr;
85 unsigned int flags;
86
87 struct timeval abs_timeout, now;
88
89 struct timeval timeout;
90 struct timeval *tm;
91 EventHandler *eh;
92
93 int r = 0;
94 int errno_save = 0;
95 int foundTimeoutEvent = 0;
96 int foundReadEvent = 0;
97 int foundWriteEvent = 0;
98 int maxfd = -1;
99 int pastDue;
100
101 /* Avoid compiler warning */
102 abs_timeout.tv_sec = 0;
103 abs_timeout.tv_usec = 0;
104
105 EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es));
106
107 /* Build the select sets */
108 FD_ZERO(&readfds);
109 FD_ZERO(&writefds);
110
111 eh = es->handlers;
112 for (eh=es->handlers; eh; eh=eh->next) {
113 if (eh->flags & EVENT_FLAG_DELETED) continue;
114 if (eh->flags & EVENT_FLAG_READABLE) {
115 foundReadEvent = 1;
116 FD_SET(eh->fd, &readfds);
117 if (eh->fd > maxfd) maxfd = eh->fd;
118 }
119 if (eh->flags & EVENT_FLAG_WRITEABLE) {
120 foundWriteEvent = 1;
121 FD_SET(eh->fd, &writefds);
122 if (eh->fd > maxfd) maxfd = eh->fd;
123 }
124 if (eh->flags & EVENT_TIMER_BITS) {
125 if (!foundTimeoutEvent) {
126 abs_timeout = eh->tmout;
127 foundTimeoutEvent = 1;
128 } else {
129 if (eh->tmout.tv_sec < abs_timeout.tv_sec ||
130 (eh->tmout.tv_sec == abs_timeout.tv_sec &&
131 eh->tmout.tv_usec < abs_timeout.tv_usec)) {
132 abs_timeout = eh->tmout;
133 }
134 }
135 }
136 }
137 if (foundReadEvent) {
138 rd = &readfds;
139 } else {
140 rd = NULL;
141 }
142 if (foundWriteEvent) {
143 wr = &writefds;
144 } else {
145 wr = NULL;
146 }
147
148 if (foundTimeoutEvent) {
149 gettimeofday(&now, NULL);
150 /* Convert absolute timeout to relative timeout for select */
151 timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec;
152 timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec;
153 if (timeout.tv_usec < 0) {
154 timeout.tv_usec += 1000000;
155 timeout.tv_sec--;
156 }
157 if (timeout.tv_sec < 0 ||
158 (timeout.tv_sec == 0 && timeout.tv_usec < 0)) {
159 timeout.tv_sec = 0;
160 timeout.tv_usec = 0;
161 }
162 tm = &timeout;
163 } else {
164 tm = NULL;
165 }
166
167 if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) {
168 for(;;) {
169 r = select(maxfd+1, rd, wr, NULL, tm);
170 if (r < 0) {
171 if (errno == EINTR) continue;
172 }
173 break;
174 }
175 }
176
177 if (foundTimeoutEvent) gettimeofday(&now, NULL);
178 errno_save = errno;
179 es->nestLevel++;
180
181 if (r >= 0) {
182 /* Call handlers */
183 for (eh=es->handlers; eh; eh=eh->next) {
184
185 /* Pending delete for this handler? Ignore it */
186 if (eh->flags & EVENT_FLAG_DELETED) continue;
187
188 flags = 0;
189 if ((eh->flags & EVENT_FLAG_READABLE) &&
190 FD_ISSET(eh->fd, &readfds)) {
191 flags |= EVENT_FLAG_READABLE;
192 }
193 if ((eh->flags & EVENT_FLAG_WRITEABLE) &&
194 FD_ISSET(eh->fd, &writefds)) {
195 flags |= EVENT_FLAG_WRITEABLE;
196 }
197 if (eh->flags & EVENT_TIMER_BITS) {
198 pastDue = (eh->tmout.tv_sec < now.tv_sec ||
199 (eh->tmout.tv_sec == now.tv_sec &&
200 eh->tmout.tv_usec <= now.tv_usec));
201 if (pastDue) {
202 flags |= EVENT_TIMER_BITS;
203 if (eh->flags & EVENT_FLAG_TIMER) {
204 /* Timer events are only called once */
205 es->opsPending = 1;
206 eh->flags |= EVENT_FLAG_DELETED;
207 }
208 }
209 }
210 /* Do callback */
211 if (flags) {
212 EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, flags));
213 eh->fn(es, eh->fd, flags, eh->data);
214 EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags));
215 }
216 }
217 }
218
219 es->nestLevel--;
220
221 if (!es->nestLevel && es->opsPending) {
222 DoPendingChanges(es);
223 }
224 errno = errno_save;
225 return r;
226}
227
228/**********************************************************************
229* %FUNCTION: Event_AddHandler
230* %ARGUMENTS:
231* es -- event selector
232* fd -- file descriptor to watch
233* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
234* fn -- callback function to call when event is triggered
235* data -- extra data to pass to callback function
236* %RETURNS:
237* A newly-allocated EventHandler, or NULL.
238***********************************************************************/
239EventHandler *
240Event_AddHandler(EventSelector *es,
241 int fd,
242 unsigned int flags,
243 EventCallbackFunc fn,
244 void *data)
245{
246 EventHandler *eh;
247
248 /* Specifically disable timer and deleted flags */
249 flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED));
250
251 /* Bad file descriptor */
252 if (fd < 0) {
253 errno = EBADF;
254 return NULL;
255 }
256
257 eh = malloc(sizeof(EventHandler));
258 if (!eh) return NULL;
259 eh->fd = fd;
260 eh->flags = flags;
261 eh->tmout.tv_usec = 0;
262 eh->tmout.tv_sec = 0;
263 eh->fn = fn;
264 eh->data = data;
265
266 /* Add immediately. This is safe even if we are in a handler. */
267 eh->next = es->handlers;
268 es->handlers = eh;
269
270 EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh));
271 return eh;
272}
273
274/**********************************************************************
275* %FUNCTION: Event_AddHandlerWithTimeout
276* %ARGUMENTS:
277* es -- event selector
278* fd -- file descriptor to watch
279* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
280* t -- Timeout after which to call handler, even if not readable/writable.
281* If t.tv_sec < 0, calls normal Event_AddHandler with no timeout.
282* fn -- callback function to call when event is triggered
283* data -- extra data to pass to callback function
284* %RETURNS:
285* A newly-allocated EventHandler, or NULL.
286***********************************************************************/
287EventHandler *
288Event_AddHandlerWithTimeout(EventSelector *es,
289 int fd,
290 unsigned int flags,
291 struct timeval t,
292 EventCallbackFunc fn,
293 void *data)
294{
295 EventHandler *eh;
296 struct timeval now;
297
298 /* If timeout is negative, just do normal non-timing-out event */
299 if (t.tv_sec < 0 || t.tv_usec < 0) {
300 return Event_AddHandler(es, fd, flags, fn, data);
301 }
302
303 /* Specifically disable timer and deleted flags */
304 flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED));
305 flags |= EVENT_FLAG_TIMEOUT;
306
307 /* Bad file descriptor? */
308 if (fd < 0) {
309 errno = EBADF;
310 return NULL;
311 }
312
313 /* Bad timeout? */
314 if (t.tv_usec >= 1000000) {
315 errno = EINVAL;
316 return NULL;
317 }
318
319 eh = malloc(sizeof(EventHandler));
320 if (!eh) return NULL;
321
322 /* Convert time interval to absolute time */
323 gettimeofday(&now, NULL);
324
325 t.tv_sec += now.tv_sec;
326 t.tv_usec += now.tv_usec;
327 if (t.tv_usec >= 1000000) {
328 t.tv_usec -= 1000000;
329 t.tv_sec++;
330 }
331
332 eh->fd = fd;
333 eh->flags = flags;
334 eh->tmout = t;
335 eh->fn = fn;
336 eh->data = data;
337
338 /* Add immediately. This is safe even if we are in a handler. */
339 eh->next = es->handlers;
340 es->handlers = eh;
341
342 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));
343 return eh;
344}
345
346
347/**********************************************************************
348* %FUNCTION: Event_AddTimerHandler
349* %ARGUMENTS:
350* es -- event selector
351* t -- time interval after which to trigger event
352* fn -- callback function to call when event is triggered
353* data -- extra data to pass to callback function
354* %RETURNS:
355* A newly-allocated EventHandler, or NULL.
356***********************************************************************/
357EventHandler *
358Event_AddTimerHandler(EventSelector *es,
359 struct timeval t,
360 EventCallbackFunc fn,
361 void *data)
362{
363 EventHandler *eh;
364 struct timeval now;
365
366 /* Check time interval for validity */
367 if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) {
368 errno = EINVAL;
369 return NULL;
370 }
371
372 eh = malloc(sizeof(EventHandler));
373 if (!eh) return NULL;
374
375 /* Convert time interval to absolute time */
376 gettimeofday(&now, NULL);
377
378 t.tv_sec += now.tv_sec;
379 t.tv_usec += now.tv_usec;
380 if (t.tv_usec >= 1000000) {
381 t.tv_usec -= 1000000;
382 t.tv_sec++;
383 }
384
385 eh->fd = -1;
386 eh->flags = EVENT_FLAG_TIMER;
387 eh->tmout = t;
388 eh->fn = fn;
389 eh->data = data;
390
391 /* Add immediately. This is safe even if we are in a handler. */
392 eh->next = es->handlers;
393 es->handlers = eh;
394
395 EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh));
396 return eh;
397}
398
399/**********************************************************************
400* %FUNCTION: Event_DelHandler
401* %ARGUMENTS:
402* es -- event selector
403* eh -- event handler
404* %RETURNS:
405* 0 if OK, non-zero if there is an error
406* %DESCRIPTION:
407* Deletes the event handler eh
408***********************************************************************/
409int
410Event_DelHandler(EventSelector *es,
411 EventHandler *eh)
412{
413 /* Scan the handlers list */
414 EventHandler *cur, *prev;
415 EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh));
416 for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) {
417 if (cur == eh) {
418 if (es->nestLevel) {
419 eh->flags |= EVENT_FLAG_DELETED;
420 es->opsPending = 1;
421 return 0;
422 } else {
423 if (prev) prev->next = cur->next;
424 else es->handlers = cur->next;
425
426 DestroyHandler(cur);
427 return 0;
428 }
429 }
430 }
431
432 /* Handler not found */
433 return 1;
434}
435
436/**********************************************************************
437* %FUNCTION: DestroySelector
438* %ARGUMENTS:
439* es -- an event selector
440* %RETURNS:
441* Nothing
442* %DESCRIPTION:
443* Destroys selector and all associated handles.
444***********************************************************************/
445void
446DestroySelector(EventSelector *es)
447{
448 EventHandler *cur, *next;
449 for (cur=es->handlers; cur; cur=next) {
450 next = cur->next;
451 DestroyHandler(cur);
452 }
453
454 free(es);
455}
456
457/**********************************************************************
458* %FUNCTION: DestroyHandler
459* %ARGUMENTS:
460* eh -- an event handler
461* %RETURNS:
462* Nothing
463* %DESCRIPTION:
464* Destroys handler
465***********************************************************************/
466void
467DestroyHandler(EventHandler *eh)
468{
469 EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh));
470 free(eh);
471}
472
473/**********************************************************************
474* %FUNCTION: DoPendingChanges
475* %ARGUMENTS:
476* es -- an event selector
477* %RETURNS:
478* Nothing
479* %DESCRIPTION:
480* Makes all pending insertions and deletions happen.
481***********************************************************************/
482void
483DoPendingChanges(EventSelector *es)
484{
485 EventHandler *cur, *prev, *next;
486
487 es->opsPending = 0;
488
489 /* If selector is to be deleted, do it and skip everything else */
490 if (es->destroyPending) {
491 DestroySelector(es);
492 return;
493 }
494
495 /* Do deletions */
496 cur = es->handlers;
497 prev = NULL;
498 while(cur) {
499 if (!(cur->flags & EVENT_FLAG_DELETED)) {
500 prev = cur;
501 cur = cur->next;
502 continue;
503 }
504
505 /* Unlink from list */
506 if (prev) {
507 prev->next = cur->next;
508 } else {
509 es->handlers = cur->next;
510 }
511 next = cur->next;
512 DestroyHandler(cur);
513 cur = next;
514 }
515}
516
517/**********************************************************************
518* %FUNCTION: Event_GetCallback
519* %ARGUMENTS:
520* eh -- the event handler
521* %RETURNS:
522* The callback function
523***********************************************************************/
524EventCallbackFunc
525Event_GetCallback(EventHandler *eh)
526{
527 return eh->fn;
528}
529
530/**********************************************************************
531* %FUNCTION: Event_GetData
532* %ARGUMENTS:
533* eh -- the event handler
534* %RETURNS:
535* The "data" field.
536***********************************************************************/
537void *
538Event_GetData(EventHandler *eh)
539{
540 return eh->data;
541}
542
543/**********************************************************************
544* %FUNCTION: Event_SetCallbackAndData
545* %ARGUMENTS:
546* eh -- the event handler
547* fn -- new callback function
548* data -- new data value
549* %RETURNS:
550* Nothing
551* %DESCRIPTION:
552* Sets the callback function and data fields.
553***********************************************************************/
554void
555Event_SetCallbackAndData(EventHandler *eh,
556 EventCallbackFunc fn,
557 void *data)
558{
559 eh->fn = fn;
560 eh->data = data;
561}
562
563#ifdef DEBUG_EVENT
564#include <stdarg.h>
565#include <stdio.h>
566FILE *Event_DebugFP = NULL;
567/**********************************************************************
568* %FUNCTION: Event_DebugMsg
569* %ARGUMENTS:
570* fmt, ... -- format string
571* %RETURNS:
572* Nothing
573* %DESCRIPTION:
574* Writes a debug message to the debug file.
575***********************************************************************/
576void
577Event_DebugMsg(char const *fmt, ...)
578{
579 va_list ap;
580 struct timeval now;
581
582 if (!Event_DebugFP) return;
583
584 gettimeofday(&now, NULL);
585
586 fprintf(Event_DebugFP, "%03d.%03d ", (int) now.tv_sec % 1000,
587 (int) now.tv_usec / 1000);
588
589 va_start(ap, fmt);
590 vfprintf(Event_DebugFP, fmt, ap);
591 va_end(ap);
592 fflush(Event_DebugFP);
593}
594
595#endif
596
597/**********************************************************************
598* %FUNCTION: Event_EnableDebugging
599* %ARGUMENTS:
600* fname -- name of file to log debug messages to
601* %RETURNS:
602* 1 if debugging was enabled; 0 otherwise.
603***********************************************************************/
604int
605Event_EnableDebugging(char const *fname)
606{
607#ifndef DEBUG_EVENT
608 return 0;
609#else
610 Event_DebugFP = fopen(fname, "w");
611 return (Event_DebugFP != NULL);
612#endif
613}
614
615/**********************************************************************
616* %FUNCTION: Event_ChangeTimeout
617* %ARGUMENTS:
618* h -- event handler
619* t -- new timeout
620* %RETURNS:
621* Nothing
622* %DESCRIPTION:
623* Changes timeout of event handler to be "t" seconds in the future.
624***********************************************************************/
625void
626Event_ChangeTimeout(EventHandler *h, struct timeval t)
627{
628 struct timeval now;
629
630 /* Check time interval for validity */
631 if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) {
632 return;
633 }
634 /* Convert time interval to absolute time */
635 gettimeofday(&now, NULL);
636
637 t.tv_sec += now.tv_sec;
638 t.tv_usec += now.tv_usec;
639 if (t.tv_usec >= 1000000) {
640 t.tv_usec -= 1000000;
641 t.tv_sec++;
642 }
643
644 h->tmout = t;
645}
646