From eca09957e470682517528b6cbbe63157aa7e588a Mon Sep 17 00:00:00 2001
From: Marek Vavrusa <marek.vavrusa@nic.cz>
Date: Fri, 5 Jul 2013 17:47:18 +0200
Subject: [PATCH] Added support for different events (READ/WRITE) for fdset.

To do:
* Disabled epoll/kqueue backend as there is something broken for write events

refs #65
---
 src/common/fdset.c        | 24 ++++++++++++------------
 src/common/fdset.h        | 14 ++++++++++++++
 src/common/fdset_epoll.c  | 29 ++++++++++++++++++++++++++++-
 src/common/fdset_epoll.h  | 11 +++++++++++
 src/common/fdset_kqueue.c | 33 ++++++++++++++++++++++++++++++---
 src/common/fdset_kqueue.h | 11 +++++++++++
 src/common/fdset_poll.c   | 33 +++++++++++++++++++++++++++++----
 src/common/fdset_poll.h   | 11 +++++++++++
 8 files changed, 146 insertions(+), 20 deletions(-)

diff --git a/src/common/fdset.c b/src/common/fdset.c
index 3b4b75aa7..96a6e51be 100644
--- a/src/common/fdset.c
+++ b/src/common/fdset.c
@@ -62,20 +62,20 @@ static void fdset_set_backend(struct fdset_backend_t *backend) {
 void __attribute__ ((constructor)) fdset_init()
 {
 	/* Preference: epoll */
-#ifdef HAVE_EPOLL_WAIT
-	if (dlsym(RTLD_DEFAULT, "epoll_wait") != 0) {
-		fdset_set_backend(&FDSET_EPOLL);
-		return;
-	}
-#endif
+//#ifdef HAVE_EPOLL_WAIT
+//	if (dlsym(RTLD_DEFAULT, "epoll_wait") != 0) {
+//		fdset_set_backend(&FDSET_EPOLL);
+//		return;
+//	}
+//#endif
 
 	/* Preference: kqueue */
-#ifdef HAVE_KQUEUE
-	if (dlsym(RTLD_DEFAULT, "kqueue") != 0) {
-		fdset_set_backend(&FDSET_KQUEUE);
-		return;
-	}
-#endif
+//#ifdef HAVE_KQUEUE
+//	if (dlsym(RTLD_DEFAULT, "kqueue") != 0) {
+//		fdset_set_backend(&FDSET_KQUEUE);
+//		return;
+//	}
+//#endif
 
 	/* Fallback: poll */
 #ifdef HAVE_POLL
diff --git a/src/common/fdset.h b/src/common/fdset.h
index e1facdb06..0edc50b33 100644
--- a/src/common/fdset.h
+++ b/src/common/fdset.h
@@ -92,6 +92,7 @@ struct fdset_backend_t
 	int (*fdset_destroy)(fdset_t*);
 	int (*fdset_add)(fdset_t*, int, int);
 	int (*fdset_remove)(fdset_t*, int);
+	int (*fdset_set_events)(fdset_t*, int, int);
 	int (*fdset_wait)(fdset_t*, int);
 	int (*fdset_begin)(fdset_t*, fdset_it_t*);
 	int (*fdset_end)(fdset_t*, fdset_it_t*);
@@ -149,6 +150,19 @@ static inline int fdset_add(fdset_t *fdset, int fd, int events) {
  */
 int fdset_remove(fdset_t *fdset, int fd);
 
+/*!
+ * \brief Change descriptor watched events mask.
+ *
+ * \param fdset Target set.
+ * \param fd Related file descriptor.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if not found.
+ */
+static inline int fdset_set_events(fdset_t *fdset, int fd, int events) {
+	return _fdset_backend.fdset_set_events(fdset, fd, events);
+}
+
 /*!
  * \brief Poll set for new events.
  *
diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c
index f889dd33c..993a432fc 100644
--- a/src/common/fdset_epoll.c
+++ b/src/common/fdset_epoll.c
@@ -81,8 +81,13 @@ int fdset_epoll_add(fdset_t *fdset, int fd, int events)
 	/* Add to epoll set. */
 	struct epoll_event ev;
 	memset(&ev, 0, sizeof(struct epoll_event));
-	ev.events = EPOLLIN;
 	ev.data.fd = fd;
+	/* Build mask. */
+	ev.events = 0;
+	if (events & OS_EV_READ)
+		ev.events = EPOLLIN;
+	if (events & OS_EV_WRITE)
+		ev.events = EPOLLOUT;
 	if (epoll_ctl(fdset->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
 		return -1;
 	}
@@ -114,6 +119,27 @@ int fdset_epoll_remove(fdset_t *fdset, int fd)
 	return 0;
 }
 
+int fdset_epoll_set_events(fdset_t *fdset, int fd, int events)
+{
+	/* Add to epoll set. */
+	struct epoll_event ev;
+	memset(&ev, 0, sizeof(struct epoll_event));
+	ev.data.fd = fd;
+
+	/* Build mask. */
+	ev.events = 0;
+	if (events & OS_EV_READ)
+		ev.events = EPOLLIN;
+	if (events & OS_EV_WRITE)
+		ev.events = EPOLLOUT;
+
+	if (epoll_ctl(fdset->epfd, EPOLL_CTL_MOD, fd, &ev) < 0) {
+		return -1;
+	}
+	return 0;
+}
+
+
 int fdset_epoll_wait(fdset_t *fdset, int timeout)
 {
 	if (fdset == NULL || fdset->nfds < 1 || fdset->events == NULL) {
@@ -195,6 +221,7 @@ struct fdset_backend_t FDSET_EPOLL = {
 	.fdset_destroy = fdset_epoll_destroy,
 	.fdset_add = fdset_epoll_add,
 	.fdset_remove = fdset_epoll_remove,
+	.fdset_set_events = fdset_epoll_set_events,
 	.fdset_wait = fdset_epoll_wait,
 	.fdset_begin = fdset_epoll_begin,
 	.fdset_end = fdset_epoll_end,
diff --git a/src/common/fdset_epoll.h b/src/common/fdset_epoll.h
index 58f25f81e..a5947378c 100644
--- a/src/common/fdset_epoll.h
+++ b/src/common/fdset_epoll.h
@@ -70,6 +70,17 @@ int fdset_epoll_add(fdset_t *fdset, int fd, int events);
  */
 int fdset_epoll_remove(fdset_t *fdset, int fd);
 
+/*!
+ * \brief Change descriptor watched events mask.
+ *
+ * \param fdset Target set.
+ * \param fd Related file descriptor.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if not found.
+ */
+int fdset_epoll_set_events(fdset_t *fdset, int fd, int events);
+
 /*!
  * \brief Poll set for new events.
  *
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
index 7c52f71ba..0685f87ae 100644
--- a/src/common/fdset_kqueue.c
+++ b/src/common/fdset_kqueue.c
@@ -91,10 +91,15 @@ int fdset_kqueue_add(fdset_t *fdset, int fd, int events)
 		return ret;
 	}
 
+	/* Build mask. */
+	int mask = 0;
+	if (events & OS_EV_READ)
+		mask |= EVFILT_READ;
+	if (events & OS_EV_WRITE)
+		mask |= EVFILT_WRITE;
+
 	/* Add to kqueue set. */
-	int evfilt = EVFILT_READ;
-	EV_SET(&fdset->events[fdset->nfds], fd, evfilt,
-	       EV_ADD|EV_ENABLE, 0, 0, 0);
+	EV_SET(&fdset->events[fdset->nfds], fd, mask, EV_ADD|EV_ENABLE, 0, 0, 0);
 	memset(fdset->revents + fdset->nfds, 0, sizeof(struct kevent));
 
 	++fdset->nfds;
@@ -154,6 +159,27 @@ int fdset_kqueue_remove(fdset_t *fdset, int fd)
 	return 0;
 }
 
+int fdset_kqueue_set_events(fdset_t *fdset, int fd, int events)
+{
+	/* Build mask. */
+	int mask = 0;
+	if (events & OS_EV_READ)
+		mask |= EVFILT_READ;
+	if (events & OS_EV_WRITE)
+		mask |= EVFILT_WRITE;
+
+	/* Find in set. */
+	struct kevent *evs = fdset->events;
+	for (int i = 0; i < fdset->nfds; ++i) {
+		if (evs[i].ident == fd) {
+			EV_SET(evs + i, fd, mask, EV_ADD|EV_ENABLE, 0, 0, 0);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
 int fdset_kqueue_wait(fdset_t *fdset, int timeout)
 {
 	if (fdset == NULL || fdset->nfds < 1 || fdset->events == NULL) {
@@ -250,6 +276,7 @@ struct fdset_backend_t FDSET_KQUEUE = {
 	.fdset_destroy = fdset_kqueue_destroy,
 	.fdset_add = fdset_kqueue_add,
 	.fdset_remove = fdset_kqueue_remove,
+	.fdset_set_events = fdset_kqueue_set_events,
 	.fdset_wait = fdset_kqueue_wait,
 	.fdset_begin = fdset_kqueue_begin,
 	.fdset_end = fdset_kqueue_end,
diff --git a/src/common/fdset_kqueue.h b/src/common/fdset_kqueue.h
index 4b650a7b9..3d6d118c4 100644
--- a/src/common/fdset_kqueue.h
+++ b/src/common/fdset_kqueue.h
@@ -70,6 +70,17 @@ int fdset_kqueue_add(fdset_t *fdset, int fd, int events);
  */
 int fdset_kqueue_remove(fdset_t *fdset, int fd);
 
+/*!
+ * \brief Change descriptor watched events mask.
+ *
+ * \param fdset Target set.
+ * \param fd Related file descriptor.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if not found.
+ */
+int fdset_kqueue_set_events(fdset_t *fdset, int fd, int events);
+
 /*!
  * \brief Poll set for new events.
  *
diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c
index e16ae11e0..d2ea86a15 100644
--- a/src/common/fdset_poll.c
+++ b/src/common/fdset_poll.c
@@ -73,8 +73,15 @@ int fdset_poll_add(fdset_t *fdset, int fd, int events)
 	/* Append. */
 	int nid = fdset->nfds++;
 	fdset->fds[nid].fd = fd;
-	fdset->fds[nid].events = POLLIN;
 	fdset->fds[nid].revents = 0;
+
+	/* Build mask. */
+	fdset->fds[nid].events = 0;
+	if (events & OS_EV_READ)
+		fdset->fds[nid].events |= POLLIN;
+	if (events & OS_EV_WRITE)
+		fdset->fds[nid].events |= POLLOUT;
+
 	return 0;
 }
 
@@ -112,6 +119,23 @@ int fdset_poll_remove(fdset_t *fdset, int fd)
 	return 0;
 }
 
+int fdset_poll_set_events(fdset_t *fdset, int fd, int events)
+{
+	struct pollfd *pfd = fdset->fds;
+	for (size_t i = 0; i < fdset->nfds; ++i) {
+		if (pfd[i].fd == fd) {
+			pfd[i].events = 0;
+			if (events & OS_EV_READ)
+				pfd[i].events |= POLLIN;
+			if (events & OS_EV_WRITE)
+				pfd[i].events |= POLLOUT;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
 int fdset_poll_wait(fdset_t *fdset, int timeout)
 {
 	if (fdset == NULL || fdset->nfds < 1 || fdset->fds == NULL) {
@@ -125,7 +149,7 @@ int fdset_poll_wait(fdset_t *fdset, int timeout)
 	/* Poll for events. */
 	int ret = poll(fdset->fds, fdset->nfds, timeout);
 	if (ret < 0) {
-		return -1;
+		return ret;
 	}
 
 	/* Set pointers for iterating. */
@@ -161,7 +185,7 @@ int fdset_poll_end(fdset_t *fdset, fdset_it_t *it)
 	/* Trace last matching item from the end. */
 	struct pollfd* pfd = fdset->fds + fdset->nfds - 1;
 	while (pfd != fdset->fds) {
-		if (pfd->events & pfd->revents) {
+		if (pfd->revents != 0) {
 			it->fd = pfd->fd;
 			it->pos = pfd - fdset->fds;
 			return 0;
@@ -183,7 +207,7 @@ int fdset_poll_next(fdset_t *fdset, fdset_it_t *it)
 	/* Find next with matching flags. */
 	for (; it->pos < fdset->nfds; ++it->pos) {
 		struct pollfd* pfd = fdset->fds + it->pos;
-		if (pfd->events & pfd->revents) {
+		if (pfd->revents != 0) {
 			it->fd = pfd->fd;
 			it->events = pfd->revents;
 			++it->pos; /* Next will start after current. */
@@ -208,6 +232,7 @@ struct fdset_backend_t FDSET_POLL = {
 	.fdset_destroy = fdset_poll_destroy,
 	.fdset_add = fdset_poll_add,
 	.fdset_remove = fdset_poll_remove,
+	.fdset_set_events = fdset_poll_set_events,
 	.fdset_wait = fdset_poll_wait,
 	.fdset_begin = fdset_poll_begin,
 	.fdset_end = fdset_poll_end,
diff --git a/src/common/fdset_poll.h b/src/common/fdset_poll.h
index 68e9e6909..4adc48670 100644
--- a/src/common/fdset_poll.h
+++ b/src/common/fdset_poll.h
@@ -70,6 +70,17 @@ int fdset_poll_add(fdset_t *fdset, int fd, int events);
  */
 int fdset_poll_remove(fdset_t *fdset, int fd);
 
+/*!
+ * \brief Change descriptor watched events mask.
+ *
+ * \param fdset Target set.
+ * \param fd Related file descriptor.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if not found.
+ */
+int fdset_poll_set_events(fdset_t *fdset, int fd, int events);
+
 /*!
  * \brief Poll set for new events.
  *
-- 
GitLab