/********************************************************************** * %FUNCTION: Event_HandleEvent * %ARGUMENTS: * es -- EventSelector * %RETURNS: * 0 if OK, non-zero on error. errno is set appropriately. * %DESCRIPTION: * Handles a single event (uses select() to wait for an event.) ***********************************************************************/ int Event_HandleEvent(EventSelector *es) { fd_set readfds, writefds; fd_set *rd, *wr; unsigned int flags; struct timeval abs_timeout = {}, now; struct timeval timeout; struct timeval *tm; EventHandler *eh; int r = 0; int errno_save = 0; int foundTimeoutEvent = 0; int foundReadEvent = 0; int foundWriteEvent = 0; int maxfd = -1; int pastDue; EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es)); /* Build the select sets */ FD_ZERO(&readfds); FD_ZERO(&writefds); eh = es->handlers; for (eh=es->handlers; eh; eh=eh->next) { if (eh->flags & EVENT_FLAG_DELETED) continue; if (eh->flags & EVENT_FLAG_READABLE) { foundReadEvent = 1; FD_SET(eh->fd, &readfds); if (eh->fd > maxfd) maxfd = eh->fd; } if (eh->flags & EVENT_FLAG_WRITEABLE) { foundWriteEvent = 1; FD_SET(eh->fd, &writefds); if (eh->fd > maxfd) maxfd = eh->fd; } if (eh->flags & EVENT_TIMER_BITS) { if (!foundTimeoutEvent) { abs_timeout = eh->tmout; foundTimeoutEvent = 1; } else { if (eh->tmout.tv_sec < abs_timeout.tv_sec || (eh->tmout.tv_sec == abs_timeout.tv_sec && eh->tmout.tv_usec < abs_timeout.tv_usec)) { abs_timeout = eh->tmout; } } } } if (foundReadEvent) { rd = &readfds; } else { rd = NULL; } if (foundWriteEvent) { wr = &writefds; } else { wr = NULL; } if (foundTimeoutEvent) { gettimeofday(&now, NULL); /* Convert absolute timeout to relative timeout for select */ timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec; timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec; if (timeout.tv_usec < 0) { timeout.tv_usec += 1000000; timeout.tv_sec--; } if (timeout.tv_sec < 0 || (timeout.tv_sec == 0 && timeout.tv_usec < 0)) { timeout.tv_sec = 0; timeout.tv_usec = 0; } tm = &timeout; } else { tm = NULL; } if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) { for(;;) { r = select(maxfd+1, rd, wr, NULL, tm); if (r < 0) { if (errno == EINTR) continue; } break; } } if (foundTimeoutEvent) gettimeofday(&now, NULL); errno_save = errno; es->nestLevel++; if (r >= 0) { /* Call handlers */ for (eh=es->handlers; eh; eh=eh->next) { /* Pending delete for this handler? Ignore it */ if (eh->flags & EVENT_FLAG_DELETED) continue; flags = 0; if ((eh->flags & EVENT_FLAG_READABLE) && FD_ISSET(eh->fd, &readfds)) { flags |= EVENT_FLAG_READABLE; } if ((eh->flags & EVENT_FLAG_WRITEABLE) && FD_ISSET(eh->fd, &writefds)) { flags |= EVENT_FLAG_WRITEABLE; } if (eh->flags & EVENT_TIMER_BITS) { pastDue = (eh->tmout.tv_sec < now.tv_sec || (eh->tmout.tv_sec == now.tv_sec && eh->tmout.tv_usec <= now.tv_usec)); if (pastDue) { flags |= EVENT_TIMER_BITS; if (eh->flags & EVENT_FLAG_TIMER) { /* Timer events are only called once */ es->opsPending = 1; eh->flags |= EVENT_FLAG_DELETED; } } } /* Do callback */ if (flags) { EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, flags)); eh->fn(es, eh->fd, flags, eh->data); EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags)); } } } es->nestLevel--; if (!es->nestLevel && es->opsPending) { DoPendingChanges(es); } errno = errno_save; return r; }