/* do a single event loop using the events defined in ev */ static int std_event_loop_once(struct tevent_context *ev, const char *location) { struct std_event_context *std_ev = talloc_get_type(ev->additional_data, struct std_event_context); struct timeval tval; if (ev->signal_events && tevent_common_check_signal(ev)) { return 0; } if (ev->immediate_events && tevent_common_loop_immediate(ev)) { return 0; } tval = tevent_common_loop_timer_delay(ev); if (tevent_timeval_is_zero(&tval)) { return 0; } epoll_check_reopen(std_ev); if (epoll_event_loop(std_ev, &tval) == 0) { return 0; } return std_event_loop_select(std_ev, &tval); }
/* event loop handling using poll() */ static int poll_event_loop_poll(struct tevent_context *ev, struct timeval *tvalp) { struct poll_event_context *poll_ev = talloc_get_type_abort( ev->additional_data, struct poll_event_context); int pollrtn; int timeout = -1; int poll_errno; struct tevent_fd *fde = NULL; unsigned i; if (ev->signal_events && tevent_common_check_signal(ev)) { return 0; } if (tvalp != NULL) { timeout = tvalp->tv_sec * 1000; timeout += (tvalp->tv_usec + 999) / 1000; } poll_event_drain_signal_fd(poll_ev); if (!poll_event_setup_fresh(ev, poll_ev)) { return -1; } tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_BEFORE_WAIT); pollrtn = poll(poll_ev->fds, poll_ev->num_fds, timeout); poll_errno = errno; tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_AFTER_WAIT); if (pollrtn == -1 && poll_errno == EINTR && ev->signal_events) { tevent_common_check_signal(ev); return 0; } if (pollrtn == 0 && tvalp) { /* we don't care about a possible delay here */ tevent_common_loop_timer_delay(ev); return 0; } if (pollrtn <= 0) { /* * No fd's ready */ return 0; } /* at least one file descriptor is ready - check which ones and call the handler, being careful to allow the handler to remove itself when called */ for (fde = ev->fd_events; fde; fde = fde->next) { unsigned idx = fde->additional_flags; struct pollfd *pfd; uint16_t flags = 0; if (idx == UINT64_MAX) { continue; } pfd = &poll_ev->fds[idx]; if (pfd->revents & POLLNVAL) { /* * the socket is dead! this should never * happen as the socket should have first been * made readable and that should have removed * the event, so this must be a bug. * * We ignore it here to match the epoll * behavior. */ tevent_debug(ev, TEVENT_DEBUG_ERROR, "POLLNVAL on fde[%p] fd[%d] - disabling\n", fde, pfd->fd); poll_ev->fdes[idx] = NULL; poll_ev->deleted = true; DLIST_REMOVE(ev->fd_events, fde); fde->event_ctx = NULL; continue; } if (pfd->revents & (POLLHUP|POLLERR)) { /* If we only wait for TEVENT_FD_WRITE, we should not tell the event handler about it, and remove the writable flag, as we only report errors when waiting for read events to match the select behavior. */ if (!(fde->flags & TEVENT_FD_READ)) { TEVENT_FD_NOT_WRITEABLE(fde); continue; } flags |= TEVENT_FD_READ; } if (pfd->revents & POLLIN) { flags |= TEVENT_FD_READ; } if (pfd->revents & POLLOUT) { flags |= TEVENT_FD_WRITE; } /* * Note that fde->flags could be changed when using * the poll_mt backend together with threads, * that why we need to check pfd->revents and fde->flags */ flags &= fde->flags; if (flags != 0) { DLIST_DEMOTE(ev->fd_events, fde, struct tevent_fd); fde->handler(ev, fde, flags, fde->private_data); return 0; } }
/* event loop handling using select() */ static int std_event_loop_select(struct std_event_context *std_ev, struct timeval *tvalp) { fd_set r_fds, w_fds; struct tevent_fd *fde; int selrtn; /* we maybe need to recalculate the maxfd */ if (std_ev->maxfd == EVENT_INVALID_MAXFD) { calc_maxfd(std_ev); } FD_ZERO(&r_fds); FD_ZERO(&w_fds); /* setup any fd events */ for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { if (fde->fd < 0 || fde->fd >= FD_SETSIZE) { std_ev->exit_code = EBADF; return -1; } if (fde->flags & TEVENT_FD_READ) { FD_SET(fde->fd, &r_fds); } if (fde->flags & TEVENT_FD_WRITE) { FD_SET(fde->fd, &w_fds); } } if (std_ev->ev->signal_events && tevent_common_check_signal(std_ev->ev)) { return 0; } selrtn = select(std_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); if (selrtn == -1 && errno == EINTR && std_ev->ev->signal_events) { tevent_common_check_signal(std_ev->ev); return 0; } if (selrtn == -1 && errno == EBADF) { /* the socket is dead! this should never happen as the socket should have first been made readable and that should have removed the event, so this must be a bug. This is a fatal error. */ tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, "ERROR: EBADF on std_event_loop_once\n"); std_ev->exit_code = EBADF; return -1; } if (selrtn == 0 && tvalp) { /* we don't care about a possible delay here */ tevent_common_loop_timer_delay(std_ev->ev); return 0; } if (selrtn > 0) { /* at least one file descriptor is ready - check which ones and call the handler, being careful to allow the handler to remove itself when called */ for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { uint16_t flags = 0; if (FD_ISSET(fde->fd, &r_fds)) flags |= TEVENT_FD_READ; if (FD_ISSET(fde->fd, &w_fds)) flags |= TEVENT_FD_WRITE; if (flags) { fde->handler(std_ev->ev, fde, flags, fde->private_data); break; } } } return 0; }
/* event loop handling using epoll */ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tvalp) { int ret, i; #define MAXEVENTS 1 struct epoll_event events[MAXEVENTS]; int timeout = -1; if (std_ev->epoll_fd == -1) return -1; if (tvalp) { /* it's better to trigger timed events a bit later than to early */ timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000); } if (std_ev->ev->signal_events && tevent_common_check_signal(std_ev->ev)) { return 0; } ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout); if (ret == -1 && errno == EINTR && std_ev->ev->signal_events) { if (tevent_common_check_signal(std_ev->ev)) { return 0; } } if (ret == -1 && errno != EINTR) { epoll_fallback_to_select(std_ev, "epoll_wait() failed"); return -1; } if (ret == 0 && tvalp) { /* we don't care about a possible delay here */ tevent_common_loop_timer_delay(std_ev->ev); return 0; } for (i=0;i<ret;i++) { struct tevent_fd *fde = talloc_get_type(events[i].data.ptr, struct tevent_fd); uint16_t flags = 0; if (fde == NULL) { epoll_fallback_to_select(std_ev, "epoll_wait() gave bad data"); return -1; } if (events[i].events & (EPOLLHUP|EPOLLERR)) { fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR; /* * if we only wait for TEVENT_FD_WRITE, we should not tell the * event handler about it, and remove the epoll_event, * as we only report errors when waiting for read events, * to match the select() behavior */ if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR)) { epoll_del_event(std_ev, fde); continue; } flags |= TEVENT_FD_READ; } if (events[i].events & EPOLLIN) flags |= TEVENT_FD_READ; if (events[i].events & EPOLLOUT) flags |= TEVENT_FD_WRITE; if (flags) { fde->handler(std_ev->ev, fde, flags, fde->private_data); break; } } return 0; }