summaryrefslogtreecommitdiff
path: root/source4/lib
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2005-12-09 20:42:09 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:47:15 -0500
commit126f8b8b6a0d065d3b3f959aa7bea2bcf0ee9c57 (patch)
treead1894f2a0daeab370374ed5e37ac007256ae30b /source4/lib
parentf600f1c68e1de3f755e929b045d08a656d57b6ce (diff)
downloadsamba-126f8b8b6a0d065d3b3f959aa7bea2bcf0ee9c57.tar.gz
samba-126f8b8b6a0d065d3b3f959aa7bea2bcf0ee9c57.tar.bz2
samba-126f8b8b6a0d065d3b3f959aa7bea2bcf0ee9c57.zip
r12153: work arround the fact that epoll reports EPOLLERR and EPOLLHUP, even if
you don't ask for. with this patch the epoll mode behaves like the select mode metze (This used to be commit f26c28a3ae7951657cc304659f3d19c16f462dd8)
Diffstat (limited to 'source4/lib')
-rw-r--r--source4/lib/events/events_standard.c97
1 files changed, 87 insertions, 10 deletions
diff --git a/source4/lib/events/events_standard.c b/source4/lib/events/events_standard.c
index abf17dd710..df2368af3e 100644
--- a/source4/lib/events/events_standard.c
+++ b/source4/lib/events/events_standard.c
@@ -119,6 +119,10 @@ static void epoll_init_ctx(struct std_event_context *std_ev, BOOL try_epoll)
talloc_set_destructor(std_ev, epoll_ctx_destructor);
}
+#define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT (1<<0)
+#define EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR (1<<1)
+#define EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR (1<<2)
+
/*
add the epoll event to the given fd_event
*/
@@ -126,40 +130,100 @@ static void epoll_add_event(struct std_event_context *std_ev, struct fd_event *f
{
struct epoll_event event;
if (std_ev->epoll_fd == -1) return;
+
+ fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+
+ /* if we don't want events yet, don't add an epoll_event */
+ if (fde->flags == 0) return;
+
ZERO_STRUCT(event);
event.events = epoll_map_flags(fde->flags);
event.data.ptr = fde;
if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) {
epoll_fallback_to_select(std_ev, "EPOLL_CTL_ADD failed");
}
+ 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 & EVENT_FD_READ) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+ }
}
/*
- remove the epoll event for given fd_event
+ delete the epoll event for given fd_event
*/
-static void epoll_remove_event(struct std_event_context *std_ev, struct fd_event *fde)
+static void epoll_del_event(struct std_event_context *std_ev, struct fd_event *fde)
{
struct epoll_event event;
if (std_ev->epoll_fd == -1) return;
+
+ fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+
+ /* if there's no epoll_event, we don't need to delete it */
+ if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT)) return;
+
ZERO_STRUCT(event);
event.events = epoll_map_flags(fde->flags);
event.data.ptr = fde;
epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event);
+ fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT;
}
/*
change the epoll event to the given fd_event
*/
-static void epoll_change_event(struct std_event_context *std_ev, struct fd_event *fde, uint16_t flags)
+static void epoll_mod_event(struct std_event_context *std_ev, struct fd_event *fde)
{
struct epoll_event event;
if (std_ev->epoll_fd == -1) return;
+
+ fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+
ZERO_STRUCT(event);
- event.events = epoll_map_flags(flags);
+ event.events = epoll_map_flags(fde->flags);
event.data.ptr = fde;
if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) {
epoll_fallback_to_select(std_ev, "EPOLL_CTL_MOD failed");
}
+
+ /* only if we want to read we want to tell the event handler about errors */
+ if (fde->flags & EVENT_FD_READ) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+ }
+}
+
+static void epoll_change_event(struct std_event_context *std_ev, struct fd_event *fde)
+{
+ BOOL got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR);
+ BOOL want_read = (fde->flags & EVENT_FD_READ);
+ BOOL want_write= (fde->flags & EVENT_FD_WRITE);
+
+ if (std_ev->epoll_fd == -1) return;
+
+ fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
+
+ /* there's already an event */
+ if (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT) {
+ if (want_read || (want_write && !got_error)) {
+ epoll_mod_event(std_ev, fde);
+ return;
+ }
+ /*
+ * if we want to match the select behavior, we need to remove the epoll_event
+ * when the caller isn't interested in events.
+ *
+ * this is because epoll reports EPOLLERR and EPOLLHUP, even without asking for them
+ */
+ epoll_del_event(std_ev, fde);
+ return;
+ }
+
+ /* there's no epoll_event attached to the fde */
+ if (want_read || (want_write && !got_error)) {
+ epoll_add_event(std_ev, fde);
+ return;
+ }
}
/*
@@ -201,7 +265,20 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
epoll_fallback_to_select(std_ev, "epoll_wait() gave bad data");
return -1;
}
- if (events[i].events & (EPOLLHUP|EPOLLERR)) flags |= EVENT_FD_READ;
+ if (events[i].events & (EPOLLHUP|EPOLLERR)) {
+ fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR;
+ /*
+ * if we only wait for EVENT_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 |= EVENT_FD_READ;
+ }
if (events[i].events & EPOLLIN) flags |= EVENT_FD_READ;
if (events[i].events & EPOLLOUT) flags |= EVENT_FD_WRITE;
if (flags) {
@@ -217,8 +294,8 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
#else
#define epoll_init_ctx(std_ev,try_epoll) if (try_epoll) {/* fix unused variable warning*/}
#define epoll_add_event(std_ev,fde)
-#define epoll_remove_event(std_ev,fde)
-#define epoll_change_event(std_ev,fde,flags)
+#define epoll_del_event(std_ev,fde)
+#define epoll_change_event(std_ev,fde)
#define epoll_event_loop(std_ev,tvalp) (-1)
#endif
@@ -280,7 +357,7 @@ static int std_event_fd_destructor(void *ptr)
DLIST_REMOVE(std_ev->fd_events, fde);
std_ev->destruction_count++;
- epoll_remove_event(std_ev, fde);
+ epoll_del_event(std_ev, fde);
return 0;
}
@@ -342,9 +419,9 @@ static void std_event_set_fd_flags(struct fd_event *fde, uint16_t flags)
ev = fde->event_ctx;
std_ev = talloc_get_type(ev->additional_data, struct std_event_context);
- epoll_change_event(std_ev, fde, flags);
-
fde->flags = flags;
+
+ epoll_change_event(std_ev, fde);
}
/*