*
* @param[in] fd The file descriptor to base the event on.
*
- * @param[in] flags #TEVENT_FD_READ or #TEVENT_FD_WRITE
+ * @param[in] flags #TEVENT_FD_READ, #TEVENT_FD_WRITE or #TEVENT_FD_ERROR.
*
* @param[in] handler The callback handler for the event.
*
*
* @param[in] fde File descriptor event to query
*
- * @return The flags set on the event. See #TEVENT_FD_READ and
- * #TEVENT_FD_WRITE
+ * @return The flags set on the event. See #TEVENT_FD_READ,
+ * #TEVENT_FD_WRITE and #TEVENT_FD_ERROR
*/
uint16_t tevent_fd_get_flags(struct tevent_fd *fde);
* Set flags on a file descriptor event
*
* @param[in] fde File descriptor event to set
- * @param[in] flags Flags to set on the event. See #TEVENT_FD_READ and
- * #TEVENT_FD_WRITE
+ * @param[in] flags Flags to set on the event. See #TEVENT_FD_READ,
+ * #TEVENT_FD_WRITE and #TEVENT_FD_ERROR
*/
void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags);
*/
#define TEVENT_FD_READ 1
/**
- * Monitor a file descriptor for data to be read
+ * Monitor a file descriptor for data to be read and errors
*/
#define TEVENT_FD_WRITE 2
+/**
+ * Monitor a file descriptor for errors
+ */
+#define TEVENT_FD_ERROR 4
/**
* Convenience function for declaring a tevent_fd writable
#define TEVENT_FD_READABLE(fde) \
tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) | TEVENT_FD_READ)
+/**
+ * Convenience function for declaring a tevent_fd waiting for errors
+ */
+#define TEVENT_FD_WANTERROR(fde) \
+ tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) | TEVENT_FD_ERROR)
/**
* Convenience function for declaring a tevent_fd non-writable
*/
#define TEVENT_FD_NOT_READABLE(fde) \
tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_READ)
+/**
+ * Convenience function for declaring a tevent_fd not waiting for errors
+ */
+#define TEVENT_FD_NOT_WANTERROR(fde) \
+ tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_ERROR)
+
/**
* Debug level of tevent
*/
static uint32_t epoll_map_flags(uint16_t flags)
{
uint32_t ret = 0;
- if (flags & TEVENT_FD_READ) ret |= (EPOLLIN | EPOLLERR | EPOLLHUP);
- if (flags & TEVENT_FD_WRITE) ret |= (EPOLLOUT | EPOLLERR | EPOLLHUP);
+ /*
+ * we do not need to specify EPOLLERR | EPOLLHUP
+ * they are always reported.
+ */
+ if (flags & TEVENT_FD_READ) ret |= EPOLLIN;
+ if (flags & TEVENT_FD_WRITE) ret |= EPOLLOUT;
return ret;
}
fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT;
/* only if we want to read we want to tell the event handler about errors */
+ if (fde->flags & TEVENT_FD_ERROR) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+ }
if (fde->flags & TEVENT_FD_READ) {
fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
}
}
/* only if we want to read we want to tell the event handler about errors */
+ if (fde->flags & TEVENT_FD_ERROR) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+ }
if (fde->flags & TEVENT_FD_READ) {
fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
}
bool got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR);
bool want_read = (fde->flags & TEVENT_FD_READ);
bool want_write= (fde->flags & TEVENT_FD_WRITE);
+ bool want_error= (fde->flags & TEVENT_FD_ERROR);
if (epoll_ev->epoll_fd == -1) return;
/* there's already an event */
if (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT) {
- if (want_read || (want_write && !got_error)) {
+ if (want_read || want_error || (want_write && !got_error)) {
epoll_mod_event(epoll_ev, fde);
return;
}
}
/* there's no epoll_event attached to the fde */
- if (want_read || (want_write && !got_error)) {
+ if (want_read || want_error || (want_write && !got_error)) {
epoll_add_event(epoll_ev, fde);
return;
}
/*
* 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
+ * as we only report errors when waiting for read events
+ * or explicit errors.
*/
if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR)) {
epoll_del_event(epoll_ev, fde);
continue;
}
- flags |= TEVENT_FD_READ;
+ if (fde->flags & TEVENT_FD_ERROR) {
+ flags |= TEVENT_FD_ERROR;
+ }
+ if (fde->flags & TEVENT_FD_READ) {
+ flags |= TEVENT_FD_READ;
+ }
}
if (events[i].events & EPOLLIN) flags |= TEVENT_FD_READ;
if (events[i].events & EPOLLOUT) flags |= TEVENT_FD_WRITE;
pfd->revents = 0;
if (flags & TEVENT_FD_READ) {
- pfd->events |= (POLLIN|POLLHUP);
+ pfd->events |= POLLIN;
}
if (flags & TEVENT_FD_WRITE) {
- pfd->events |= (POLLOUT);
+ pfd->events |= POLLOUT;
}
fde->additional_flags = poll_ev->num_fds;
uint64_t idx = fde->additional_flags;
uint16_t pollflags = 0;
+ /*
+ * we do not need to specify POLLERR | POLLHUP
+ * they are always reported.
+ */
if (flags & TEVENT_FD_READ) {
- pollflags |= (POLLIN|POLLHUP);
+ pollflags |= POLLIN;
}
if (flags & TEVENT_FD_WRITE) {
- pollflags |= (POLLOUT);
+ pollflags |= POLLOUT;
}
poll_ev->fds[idx].events = pollflags;
pfd = &poll_ev->fds[pfd_idx];
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)) {
+ /*
+ * 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
+ * or explicit for errors.
+ */
+ if ((fde->flags & ~TEVENT_FD_WRITE) == 0) {
TEVENT_FD_NOT_WRITEABLE(fde);
continue;
}
- flags |= TEVENT_FD_READ;
+ if (fde->flags & TEVENT_FD_ERROR) {
+ flags |= TEVENT_FD_ERROR;
+ }
+ if (fde->flags & TEVENT_FD_READ) {
+ flags |= TEVENT_FD_READ;
+ }
}
if (pfd->revents & POLLIN) {
flags |= TEVENT_FD_READ;
if (fde->flags & TEVENT_FD_READ) {
FD_SET(fde->fd, &r_fds);
+ } else if (fde->flags & TEVENT_FD_ERROR) {
+ FD_SET(fde->fd, &r_fds);
}
if (fde->flags & TEVENT_FD_WRITE) {
FD_SET(fde->fd, &w_fds);
for (fde = select_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 (fde->flags == 0) {
+ continue;
+ }
+
+ if (FD_ISSET(fde->fd, &r_fds)) {
+ if (fde->flags & TEVENT_FD_ERROR) {
+ int err = errno;
+ int available = 0;
+ int ret;
+
+ /* support for POLLHUP */
+ ret = ioctl(fde->fd, FIONREAD, &available);
+ errno = err;
+ if ((ret == -1) || (available == 0)) {
+ flags |= TEVENT_FD_ERROR;
+ }
+ }
+ if (fde->flags & TEVENT_FD_READ) {
+ flags |= TEVENT_FD_READ;
+ }
+ }
+ if (FD_ISSET(fde->fd, &w_fds)) {
+ flags |= TEVENT_FD_WRITE;
+ }
if (flags) {
fde->handler(select_ev->ev, fde, flags, fde->private_data);
break;
static uint32_t epoll_map_flags(uint16_t flags)
{
uint32_t ret = 0;
- if (flags & TEVENT_FD_READ) ret |= (EPOLLIN | EPOLLERR | EPOLLHUP);
- if (flags & TEVENT_FD_WRITE) ret |= (EPOLLOUT | EPOLLERR | EPOLLHUP);
+ /*
+ * we do not need to specify EPOLLERR | EPOLLHUP
+ * they are always reported.
+ */
+ if (flags & TEVENT_FD_READ) ret |= EPOLLIN;
+ if (flags & TEVENT_FD_WRITE) ret |= EPOLLOUT;
return ret;
}
fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT;
/* only if we want to read we want to tell the event handler about errors */
+ if (fde->flags & TEVENT_FD_ERROR) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+ }
if (fde->flags & TEVENT_FD_READ) {
fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
}
}
/* only if we want to read we want to tell the event handler about errors */
+ if (fde->flags & TEVENT_FD_ERROR) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+ }
if (fde->flags & TEVENT_FD_READ) {
fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
}
bool got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR);
bool want_read = (fde->flags & TEVENT_FD_READ);
bool want_write= (fde->flags & TEVENT_FD_WRITE);
+ bool want_error= (fde->flags & TEVENT_FD_ERROR);
if (std_ev->epoll_fd == -1) return;
/* there's already an event */
if (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT) {
- if (want_read || (want_write && !got_error)) {
+ if (want_read || want_error || (want_write && !got_error)) {
epoll_mod_event(std_ev, fde);
return;
}
}
/* there's no epoll_event attached to the fde */
- if (want_read || (want_write && !got_error)) {
+ if (want_read || want_error || (want_write && !got_error)) {
epoll_add_event(std_ev, fde);
return;
}
/*
* 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
+ * as we only report errors when waiting for read events
+ * or explicit errors.
*/
if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR)) {
epoll_del_event(std_ev, fde);
continue;
}
- flags |= TEVENT_FD_READ;
+ if (fde->flags & TEVENT_FD_ERROR) {
+ flags |= TEVENT_FD_ERROR;
+ }
+ if (fde->flags & TEVENT_FD_READ) {
+ flags |= TEVENT_FD_READ;
+ }
}
if (events[i].events & EPOLLIN) flags |= TEVENT_FD_READ;
if (events[i].events & EPOLLOUT) flags |= TEVENT_FD_WRITE;
}
if (fde->flags & TEVENT_FD_READ) {
FD_SET(fde->fd, &r_fds);
+ } else if (fde->flags & TEVENT_FD_ERROR) {
+ FD_SET(fde->fd, &r_fds);
}
if (fde->flags & TEVENT_FD_WRITE) {
FD_SET(fde->fd, &w_fds);
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->flags) {
+ if (fde->flags == 0) {
+ continue;
+ }
+
+ if (FD_ISSET(fde->fd, &r_fds)) {
+ if (fde->flags & TEVENT_FD_ERROR) {
+ int err = errno;
+ int available = 0;
+ int ret;
+
+ /* support for POLLHUP */
+ ret = ioctl(fde->fd, FIONREAD, &available);
+ errno = err;
+ if ((ret == -1) || (available == 0)) {
+ flags |= TEVENT_FD_ERROR;
+ }
+ }
+ if (fde->flags & TEVENT_FD_READ) {
+ 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;
}