#include "includes.h"
#include "system/filesys.h"
+#include "system/select.h" /* needed for WITH_EPOLL */
#include "dlinklist.h"
#include "lib/events/events.h"
#include "lib/events/events_internal.h"
-/* use epoll if it is available */
-#if defined(HAVE_EPOLL_CREATE) && defined(HAVE_SYS_EPOLL_H)
-#define WITH_EPOLL 1
-#endif
-
-#if WITH_EPOLL
-#include <sys/epoll.h>
-#endif
-
struct std_event_context {
+ /* a pointer back to the generic event_context */
+ struct event_context *ev;
+
/* list of filedescriptor events */
struct fd_event *fd_events;
*/
uint32_t destruction_count;
-#if WITH_EPOLL
/* when using epoll this is the handle from epoll_create */
int epoll_fd;
-#endif
};
+static void std_event_loop_timer(struct std_event_context *std_ev);
+
+/* use epoll if it is available */
+#if WITH_EPOLL
/*
- destroy an event context
+ called when a epoll call fails, and we should fallback
+ to using select
*/
-static int std_event_context_destructor(void *ptr)
+static void epoll_fallback_to_select(struct std_event_context *std_ev, const char *reason)
{
-#if WITH_EPOLL
- struct event_context *ev = talloc_get_type(ptr, struct event_context);
- struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
+ DEBUG(0,("%s (%s) - falling back to select()\n", reason, strerror(errno)));
+ close(std_ev->epoll_fd);
+ std_ev->epoll_fd = -1;
+ talloc_set_destructor(std_ev, NULL);
+}
+
+/*
+ map from EVENT_FD_* to EPOLLIN/EPOLLOUT
+*/
+static uint32_t epoll_map_flags(uint16_t flags)
+{
+ uint32_t ret = 0;
+ if (flags & EVENT_FD_READ) ret |= (EPOLLIN | EPOLLERR | EPOLLHUP);
+ if (flags & EVENT_FD_WRITE) ret |= (EPOLLOUT | EPOLLERR | EPOLLHUP);
+ return ret;
+}
+
+/*
+ free the epoll fd
+*/
+static int epoll_ctx_destructor(void *ptr)
+{
+ struct std_event_context *std_ev = talloc_get_type(ptr,
struct std_event_context);
- if (std_ev->epoll_fd != -1) {
- close(std_ev->epoll_fd);
- std_ev->epoll_fd = -1;
+ close(std_ev->epoll_fd);
+ std_ev->epoll_fd = -1;
+ return 0;
+}
+
+/*
+ init the epoll fd
+*/
+static void epoll_init_ctx(struct std_event_context *std_ev, BOOL try_epoll)
+{
+ if (!try_epoll) return;
+ std_ev->epoll_fd = epoll_create(64);
+ 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
+*/
+static void epoll_add_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 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");
}
-#endif
+ 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;
+ }
+}
+
+/*
+ delete the epoll event for given fd_event
+*/
+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_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(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;
+ }
+}
+
+/*
+ event loop handling using epoll
+*/
+static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tvalp)
+{
+ int ret, i;
+#define MAXEVENTS 8
+ struct epoll_event events[MAXEVENTS];
+ uint32_t destruction_count = std_ev->destruction_count;
+ 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);
+ }
+
+ ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout);
+
+ if (ret == -1 && errno != EINTR) {
+ epoll_fallback_to_select(std_ev, "epoll_wait() failed");
+ return -1;
+ }
+
+ if (ret == 0 && tvalp) {
+ std_event_loop_timer(std_ev);
+ return 0;
+ }
+
+ for (i=0;i<ret;i++) {
+ struct fd_event *fde = talloc_get_type(events[i].data.ptr,
+ struct fd_event);
+ 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 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) {
+ fde->handler(std_ev->ev, fde, flags, fde->private_data);
+ if (destruction_count != std_ev->destruction_count) {
+ break;
+ }
+ }
+ }
+
return 0;
}
+#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_del_event(std_ev,fde)
+#define epoll_change_event(std_ev,fde)
+#define epoll_event_loop(std_ev,tvalp) (-1)
+#endif
/*
create a std_event_context structure.
*/
-static int std_event_context_init(struct event_context *ev, void *privata_data)
+static int std_event_context_init(struct event_context *ev, void *private_data)
{
struct std_event_context *std_ev;
+ BOOL *_try_epoll = private_data;
+ BOOL try_epoll = (_try_epoll == NULL ? True : *_try_epoll);
std_ev = talloc_zero(ev, struct std_event_context);
if (!std_ev) return -1;
+ std_ev->ev = ev;
+ std_ev->epoll_fd = -1;
-#if WITH_EPOLL
- std_ev->epoll_fd = epoll_create(64);
-#endif
+ epoll_init_ctx(std_ev, try_epoll);
ev->additional_data = std_ev;
-
- talloc_set_destructor(ev, std_event_context_destructor);
-
return 0;
}
/*
recalculate the maxfd
*/
-static void calc_maxfd(struct event_context *ev)
+static void calc_maxfd(struct std_event_context *std_ev)
{
- struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
- struct std_event_context);
struct fd_event *fde;
std_ev->maxfd = 0;
*/
#define EVENT_INVALID_MAXFD (-1)
-
-#if WITH_EPOLL
-/*
- called when a epoll call fails, and we should fallback
- to using select
-*/
-static void epoll_fallback_to_select(struct event_context *ev, const char *reason)
-{
- struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
- struct std_event_context);
- DEBUG(0,("%s (%s) - falling back to select()\n", reason, strerror(errno)));
- close(std_ev->epoll_fd);
- std_ev->epoll_fd = -1;
-}
-#endif
-
-
-#if WITH_EPOLL
-/*
- map from EVENT_FD_* to EPOLLIN/EPOLLOUT
-*/
-static uint32_t epoll_map_flags(uint16_t flags)
-{
- uint32_t ret = 0;
- if (flags & EVENT_FD_READ) ret |= EPOLLIN;
- if (flags & EVENT_FD_WRITE) ret |= EPOLLOUT;
- return ret;
-}
-#endif
-
/*
destroy an fd_event
*/
if (std_ev->maxfd == fde->fd) {
std_ev->maxfd = EVENT_INVALID_MAXFD;
}
+
DLIST_REMOVE(std_ev->fd_events, fde);
std_ev->destruction_count++;
-#if WITH_EPOLL
- if (std_ev->epoll_fd != -1) {
- struct epoll_event event;
- 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);
- }
-#endif
+
+ epoll_del_event(std_ev, fde);
+
return 0;
}
fde->flags = flags;
fde->handler = handler;
fde->private_data = private_data;
+ fde->additional_flags = 0;
fde->additional_data = NULL;
DLIST_ADD(std_ev->fd_events, fde);
-
if (fde->fd > std_ev->maxfd) {
std_ev->maxfd = fde->fd;
}
-
talloc_set_destructor(fde, std_event_fd_destructor);
-#if WITH_EPOLL
- if (std_ev->epoll_fd != -1) {
- struct epoll_event event;
- ZERO_STRUCT(event);
- event.events = epoll_map_flags(flags);
- event.data.ptr = fde;
- if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) {
- epoll_fallback_to_select(ev, "EPOLL_CTL_ADD failed");
- }
- }
-#endif
+ epoll_add_event(std_ev, fde);
return fde;
}
*/
static uint16_t std_event_get_fd_flags(struct fd_event *fde)
{
- return fde?fde->flags:0;
+ return fde->flags;
}
/*
*/
static void std_event_set_fd_flags(struct fd_event *fde, uint16_t flags)
{
-#if WITH_EPOLL
struct event_context *ev;
struct std_event_context *std_ev;
- if (fde == NULL ||
- fde->flags == flags) {
- return;
- }
+
+ if (fde->flags == flags) return;
+
ev = fde->event_ctx;
std_ev = talloc_get_type(ev->additional_data, struct std_event_context);
- if (std_ev->epoll_fd != -1) {
- struct epoll_event event;
- ZERO_STRUCT(event);
- event.events = epoll_map_flags(flags);
- event.data.ptr = fde;
- if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) {
- epoll_fallback_to_select(ev, "EPOLL_CTL_MOD failed");
- }
- }
-#endif
- if (fde) {
- fde->flags = flags;
- }
+
+ fde->flags = flags;
+
+ epoll_change_event(std_ev, fde);
}
/*
/*
a timer has gone off - call it
*/
-static void std_event_loop_timer(struct event_context *ev)
+static void std_event_loop_timer(struct std_event_context *std_ev)
{
- struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
- struct std_event_context);
struct timeval t = timeval_current();
struct timed_event *te = std_ev->timed_events;
* handler we don't want to come across this event again -- vl */
DLIST_REMOVE(std_ev->timed_events, te);
- te->handler(ev, te, t, te->private_data);
+ te->handler(std_ev->ev, te, t, te->private_data);
/* The destructor isn't necessary anymore, we've already removed the
* event from the list. */
+ talloc_set_destructor(te, NULL);
talloc_free(te);
}
-#if WITH_EPOLL
-/*
- event loop handling using epoll
-*/
-static int std_event_loop_epoll(struct event_context *ev, struct timeval *tvalp)
-{
- struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
- struct std_event_context);
- int ret, i;
-#define MAXEVENTS 8
- struct epoll_event events[MAXEVENTS];
- uint32_t destruction_count = std_ev->destruction_count;
- int timeout = -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);
- }
-
- ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout);
-
- if (ret == -1 && errno != EINTR) {
- epoll_fallback_to_select(ev, "epoll_wait() failed");
- return -1;
- }
-
- if (ret == 0 && tvalp) {
- std_event_loop_timer(ev);
- return 0;
- }
-
- for (i=0;i<ret;i++) {
- struct fd_event *fde = talloc_get_type(events[i].data.ptr,
- struct fd_event);
- uint16_t flags = 0;
-
- if (fde == NULL) {
- epoll_fallback_to_select(ev, "epoll_wait() gave bad data");
- return -1;
- }
- if (events[i].events & (EPOLLIN|EPOLLHUP|EPOLLERR))
- flags |= EVENT_FD_READ;
- if (events[i].events & EPOLLOUT) flags |= EVENT_FD_WRITE;
- if (flags) {
- fde->handler(ev, fde, flags, fde->private_data);
- if (destruction_count != std_ev->destruction_count) {
- break;
- }
- }
- }
-
- return 0;
-}
-#endif
-
/*
event loop handling using select()
*/
-static int std_event_loop_select(struct event_context *ev, struct timeval *tvalp)
+static int std_event_loop_select(struct std_event_context *std_ev, struct timeval *tvalp)
{
- struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
- struct std_event_context);
fd_set r_fds, w_fds;
struct fd_event *fde;
int selrtn;
/* we maybe need to recalculate the maxfd */
if (std_ev->maxfd == EVENT_INVALID_MAXFD) {
- calc_maxfd(ev);
+ calc_maxfd(std_ev);
}
FD_ZERO(&r_fds);
}
if (selrtn == 0 && tvalp) {
- std_event_loop_timer(ev);
+ std_event_loop_timer(std_ev);
return 0;
}
if (FD_ISSET(fde->fd, &r_fds)) flags |= EVENT_FD_READ;
if (FD_ISSET(fde->fd, &w_fds)) flags |= EVENT_FD_WRITE;
if (flags) {
- fde->handler(ev, fde, flags, fde->private_data);
+ fde->handler(std_ev->ev, fde, flags, fde->private_data);
if (destruction_count != std_ev->destruction_count) {
break;
}
struct timeval t = timeval_current();
tval = timeval_until(&t, &std_ev->timed_events->next_event);
if (timeval_is_zero(&tval)) {
- std_event_loop_timer(ev);
+ std_event_loop_timer(std_ev);
return 0;
}
} else {
tval = timeval_set(30, 0);
}
-#if WITH_EPOLL
- if (std_ev->epoll_fd != -1) {
- if (std_event_loop_epoll(ev, &tval) == 0) {
- return 0;
- }
+ if (epoll_event_loop(std_ev, &tval) == 0) {
+ return 0;
}
-#endif
- return std_event_loop_select(ev, &tval);
+ return std_event_loop_select(std_ev, &tval);
}
/*