TODO testing tevent: add support for TEVENT_FD_ERROR
authorStefan Metzmacher <metze@samba.org>
Wed, 13 Jul 2011 07:46:26 +0000 (09:46 +0200)
committerStefan Metzmacher <metze@samba.org>
Mon, 18 Jul 2011 07:02:11 +0000 (09:02 +0200)
metze

lib/tevent/tevent.h
lib/tevent/tevent_epoll.c
lib/tevent/tevent_poll.c
lib/tevent/tevent_select.c
lib/tevent/tevent_standard.c

index 7ad566c69ea08da73c243401df39ba5a30a15a3a..0720109e81de472e39ed83c226b72233cbd6912d 100644 (file)
@@ -152,7 +152,7 @@ void tevent_set_default_backend(const char *backend);
  *
  * @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.
  *
@@ -389,8 +389,8 @@ void tevent_fd_set_auto_close(struct tevent_fd *fde);
  *
  * @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);
 
@@ -398,8 +398,8 @@ 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);
 
@@ -421,9 +421,13 @@ void tevent_set_abort_fn(void (*abort_fn)(const char *reason));
  */
 #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
@@ -437,6 +441,11 @@ void tevent_set_abort_fn(void (*abort_fn)(const char *reason));
 #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
  */
@@ -449,6 +458,12 @@ void tevent_set_abort_fn(void (*abort_fn)(const char *reason));
 #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
  */
index 3ab828395586f6a8ce1d4563678b58ad24cd7ec7..78980827d8e8923707fbf747d989dfab1536284c 100644 (file)
@@ -57,8 +57,12 @@ static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason
 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;
 }
 
@@ -141,6 +145,9 @@ static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_
        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;
        }
@@ -189,6 +196,9 @@ static void epoll_mod_event(struct epoll_event_context *epoll_ev, struct tevent_
        }
 
        /* 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;
        }
@@ -199,6 +209,7 @@ static void epoll_change_event(struct epoll_event_context *epoll_ev, struct teve
        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;
 
@@ -206,7 +217,7 @@ static void epoll_change_event(struct epoll_event_context *epoll_ev, struct teve
 
        /* 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;
                }
@@ -221,7 +232,7 @@ static void epoll_change_event(struct epoll_event_context *epoll_ev, struct teve
        }
 
        /* 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;
        }
@@ -282,14 +293,19 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval
                        /*
                         * 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;
index 0b782e99bb82b2b7bd2eba8cec9fa8a3fcf69081..03de913effaddbe4a9746d08ecff5a2fb31ecbe9 100644 (file)
@@ -138,10 +138,10 @@ static struct tevent_fd *poll_event_add_fd(struct tevent_context *ev,
        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;
@@ -164,11 +164,15 @@ static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
        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;
@@ -234,16 +238,23 @@ static int poll_event_loop_poll(struct tevent_context *ev,
                        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;
index 94faa8659340a7d5ab1bb4d0833520a8b56ebe09..6d85ca94e915b49043e13ec19a5de28a8de6ad83 100644 (file)
@@ -156,6 +156,8 @@ static int select_event_loop_select(struct select_event_context *select_ev, stru
 
                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);
@@ -200,8 +202,30 @@ static int select_event_loop_select(struct select_event_context *select_ev, stru
                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;
index 35f7ded9b703792e4abfdf389bf9c74b0e1bde4e..1132520e79a052ab4fe08c9723c4b7eaca81daed 100644 (file)
@@ -77,8 +77,12 @@ static void epoll_fallback_to_select(struct std_event_context *std_ev, const cha
 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;
 }
 
@@ -158,6 +162,9 @@ static void epoll_add_event(struct std_event_context *std_ev, struct tevent_fd *
        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;
        }
@@ -201,6 +208,9 @@ static void epoll_mod_event(struct std_event_context *std_ev, struct tevent_fd *
        }
 
        /* 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;
        }
@@ -211,6 +221,7 @@ static void epoll_change_event(struct std_event_context *std_ev, struct tevent_f
        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;
 
@@ -218,7 +229,7 @@ static void epoll_change_event(struct std_event_context *std_ev, struct tevent_f
 
        /* 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;
                }
@@ -233,7 +244,7 @@ static void epoll_change_event(struct std_event_context *std_ev, struct tevent_f
        }
 
        /* 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;
        }
@@ -294,14 +305,19 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
                        /*
                         * 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;
@@ -463,6 +479,8 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva
                }
                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);
@@ -507,9 +525,31 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva
                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;
                        }