changeset 370:2c6a4f468499

Socket: add epoll method
author David Demelier <markand@malikania.fr>
date Wed, 29 Apr 2015 17:17:35 +0200
parents f3c762579073
children 166943bde655
files C++/modules/Socket/SocketListener.cpp C++/modules/Socket/SocketListener.h C++/tests/Socket/main.cpp
diffstat 3 files changed, 197 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Socket/SocketListener.cpp	Wed Apr 29 14:04:58 2015 +0200
+++ b/C++/modules/Socket/SocketListener.cpp	Wed Apr 29 17:17:35 2015 +0200
@@ -27,18 +27,7 @@
 
 namespace backend {
 
-SocketStatus Select::wait(const SocketTable &table, int ms)
-{
-	std::vector<SocketStatus> result = waitMultiple(table, ms);
-
-	if (result.size() == 0) {
-		throw SocketError(SocketError::System, "select", "No socket found");
-	}
-
-	return result[0];
-}
-
-std::vector<SocketStatus> Select::waitMultiple(const SocketTable &table, int ms)
+std::vector<SocketStatus> Select::wait(const SocketTable &table, int ms)
 {
 	timeval maxwait, *towait;
 	fd_set readset;
@@ -138,7 +127,7 @@
 	return flags;
 }
 
-void Poll::set(Socket s, int flags, bool add)
+void Poll::set(const SocketTable &, Socket s, int flags, bool add)
 {
 	if (add) {
 		m_lookup.emplace(s.handle(), s);
@@ -152,7 +141,7 @@
 	}
 }
 
-void Poll::unset(Socket s, int flags, bool remove)
+void Poll::unset(const SocketTable &, Socket s, int flags, bool remove)
 {
 	auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const struct pollfd &pfd) {
 		return pfd.fd == s.handle();
@@ -166,26 +155,7 @@
 	}
 }
 
-SocketStatus Poll::wait(const SocketTable &table, int ms)
-{
-	auto result = poll(m_fds.data(), m_fds.size(), ms);
-	if (result == 0) {
-		throw SocketError(SocketError::Timeout, "select", "Timeout while listening");
-	}
-	if (result < 0) {
-		throw SocketError(SocketError::System, "poll");
-	}
-
-	for (auto &fd : m_fds) {
-		if (fd.revents != 0) {
-			return SocketStatus{m_lookup.at(fd.fd), toflags(fd.revents)};
-		}
-	}
-
-	throw SocketError(SocketError::System, "select", "No socket found");
-}
-
-std::vector<SocketStatus> Poll::waitMultiple(const SocketTable &, int ms)
+std::vector<SocketStatus> Poll::wait(const SocketTable &, int ms)
 {
 	auto result = poll(m_fds.data(), m_fds.size(), ms);
 	if (result == 0) {
@@ -208,6 +178,118 @@
 #endif // !SOCKET_HAVE_POLL
 
 /* --------------------------------------------------------
+ * Epoll implementation
+ * -------------------------------------------------------- */
+
+#if defined(SOCKET_HAVE_EPOLL)
+
+uint32_t Epoll::toepoll(int flags) const noexcept
+{
+	uint32_t events = 0;
+
+	if (flags & SocketListener::Read) {
+		events |= EPOLLIN;
+	}
+	if (flags & SocketListener::Write) {
+		events |= EPOLLOUT;
+	}
+
+	return events;
+}
+
+int Epoll::toflags(uint32_t events) const noexcept
+{
+	int flags = 0;
+
+	if ((events & EPOLLIN) || (events & EPOLLHUP)) {
+		flags |= SocketListener::Read;
+	}
+	if (events & EPOLLOUT) {
+		flags |= SocketListener::Write;
+	}
+
+	return flags;
+}
+
+void Epoll::update(Socket &sc, int op, int flags)
+{
+	struct epoll_event ev;
+
+	std::memset(&ev, 0, sizeof (struct epoll_event));
+
+	ev.events = flags;
+	ev.data.fd = sc.handle();
+
+	if (epoll_ctl(m_handle, op, sc.handle(), &ev) < 0) {
+		throw SocketError(SocketError::System, "epoll_ctl");
+	}
+}
+
+Epoll::Epoll()
+	: m_handle(epoll_create1(0))
+{
+	if (m_handle < 0) {
+		throw SocketError(SocketError::System, "epoll_create");
+	}
+}
+
+Epoll::~Epoll()
+{
+	close(m_handle);
+}
+
+/*
+ * Add a new epoll_event or just update it.
+ */
+void Epoll::set(const SocketTable &, Socket &sc, int flags, bool add)
+{
+	update(sc, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, toepoll(flags));
+
+	if (add) {
+		m_events.resize(m_events.size() + 1);
+	}
+}
+
+/*
+ * Unset is a bit complicated case because SocketListener tells us which
+ * flag to remove but to update epoll descriptor we need to pass
+ * the effective flags that we want to be applied.
+ *
+ * So we put the same flags that are currently effective and remove the
+ * requested one.
+ */
+void Epoll::unset(const SocketTable &table, Socket &sc, int flags, bool remove)
+{
+	if (remove) {
+		update(sc, EPOLL_CTL_DEL, 0);
+		m_events.resize(m_events.size() - 1);
+	} else {
+		update(sc, EPOLL_CTL_MOD, table.at(sc.handle()).second & ~(toepoll(flags)));
+	}
+}
+
+std::vector<SocketStatus> Epoll::wait(const SocketTable &table, int ms)
+{
+	int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms);
+	std::vector<SocketStatus> result;
+
+	if (ret == 0) {
+		throw SocketError(SocketError::Timeout, "epoll_wait");
+	}
+	if (ret < 0) {
+		throw SocketError(SocketError::System, "epoll_wait");
+	}
+
+	for (int i = 0; i < ret; ++i) {
+		result.push_back(SocketStatus{table.at(m_events[i].data.fd).first, toflags(m_events[i].events)});
+	}
+
+	return result;
+}
+
+#endif // !SOCKET_HAVE_EPOLL
+
+/* --------------------------------------------------------
  * Kqueue implementation
  * -------------------------------------------------------- */
 
--- a/C++/modules/Socket/SocketListener.h	Wed Apr 29 14:04:58 2015 +0200
+++ b/C++/modules/Socket/SocketListener.h	Wed Apr 29 17:17:35 2015 +0200
@@ -54,7 +54,7 @@
  * +---------------+-------------------------+
  * | System        | Backend                 |
  * +---------------+-------------------------+
- * | Linux         | epoll(2)                |
+ * | Linux         | epoll(7)                |
  * | *BSD          | kqueue(2)               |
  * | Windows       | poll(2), select(2)      |
  * | Mac OS X      | kqueue(2)               |
@@ -68,14 +68,15 @@
 #    define SOCKET_DEFAULT_BACKEND backend::Select
 #  endif
 #elif defined(__linux__)
-//#  define SOCKET_DEFAULT_BACKEND backend::Epoll
-#  define SOCKET_DEFAULT_BACKEND backend::Select
-#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
-#  include <sys/stat.h>
+#  include <sys/epoll.h>
+#  include <cstring>
+
+#  define SOCKET_DEFAULT_BACKEND backend::Epoll
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__)
+#  include <sys/types.h>
 #  include <sys/event.h>
 #  include <sys/time.h>
 
-//#  define SOCKET_DEFAULT_BACKEND backend::Kqueue
 #  define SOCKET_DEFAULT_BACKEND backend::Select
 #else
 #  define SOCKET_DEFAULT_BACKEND backend::Select
@@ -124,18 +125,25 @@
  */
 class Select {
 public:
-	/*
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "select";
+	}
+
+	/**
 	 * No-op, uses the SocketTable directly.
 	 */
-	inline void set(Socket, int, bool) noexcept {}
+	inline void set(const SocketTable &, const Socket &, int, bool) noexcept {}
 
-	/*
+	/**
 	 * No-op, uses the SocketTable directly.
 	 */
-	inline void unset(Socket, int, bool) noexcept {}
+	inline void unset(const SocketTable &, const Socket &, int, bool) noexcept {}
 
-	SocketStatus wait(const SocketTable &table, int ms);
-	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms);
+	std::vector<SocketStatus> wait(const SocketTable &table, int ms);
 };
 
 #if defined(SOCKET_HAVE_POLL)
@@ -156,15 +164,45 @@
 	int toflags(short &event) const noexcept;
 
 public:
-	void set(Socket sc, int flags, bool add);
-	void unset(Socket sc, int flags, bool remove);
-	SocketStatus wait(const SocketTable &, int ms);
-	std::vector<SocketStatus> waitMultiple(const SocketTable &, int ms);
+	void set(const SocketTable &, Socket sc, int flags, bool add);
+	void unset(const SocketTable &, Socket sc, int flags, bool remove);
+	std::vector<SocketStatus> wait(const SocketTable &, int ms);
 };
 
 #endif
 
 #if defined(SOCKET_HAVE_EPOLL)
+
+class Epoll {
+private:
+	int m_handle;
+	std::vector<struct epoll_event> m_events;
+
+	Epoll(const Epoll &) = delete;
+	Epoll &operator=(const Epoll &) = delete;
+	Epoll(const Epoll &&) = delete;
+	Epoll &operator=(const Epoll &&) = delete;
+
+	uint32_t toepoll(int flags) const noexcept;
+	int toflags(uint32_t events) const noexcept;
+	void update(Socket &sc, int op, int flags);
+
+public:
+	Epoll();
+	~Epoll();
+	void set(const SocketTable &, Socket &sc, int flags, bool add);
+	void unset(const SocketTable &, Socket &sc, int flags, bool remove);
+	std::vector<SocketStatus> wait(const SocketTable &table, int ms);
+
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "epoll";
+	}
+};
+
 #endif
 
 #if defined(SOCKET_HAVE_KQUEUE)
@@ -196,6 +234,14 @@
 	void unset(Socket sc, int flags, bool remove);
 	SocketStatus wait(int ms);
 	std::vector<SocketStatus> waitMultiple(int ms);
+
+	/**
+	 * Backend identifier
+	 */
+	inline const char *name() const noexcept
+	{
+		return "kqueue";
+	}
 };
 
 #endif
@@ -403,7 +449,7 @@
 	{
 		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
 
-		return m_backend.wait(cvt.count());
+		return m_backend.wait(m_table, cvt.count())[0];
 	}
 
 	/**
@@ -414,7 +460,7 @@
 	 */
 	inline SocketStatus wait(int timeout = -1)
 	{
-		return m_backend.wait(m_table, timeout);
+		return wait(std::chrono::milliseconds(timeout));
 	}
 
 	/**
@@ -428,7 +474,7 @@
 	{
 		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
 
-		return m_backend.waitMultiple(m_table, cvt.count());
+		return m_backend.wait(m_table, cvt.count());
 	}
 
 	/**
@@ -438,7 +484,7 @@
 	 */
 	inline std::vector<SocketStatus> waitMultiple(int timeout = -1)
 	{
-		return m_backend.waitMultiple(m_table, timeout);
+		return waitMultiple(std::chrono::milliseconds(timeout));
 	}
 };
 
@@ -456,7 +502,7 @@
 	 * or update.
 	 */
 	if (it == m_table.end()) {
-		m_backend.set(sc, flags, true);
+		m_backend.set(m_table, sc, flags, true);
 		m_table.emplace(sc.handle(), std::make_pair(sc, flags));
 	} else {
 		if ((flags & Read) && (it->second.second & Read)) {
@@ -468,7 +514,7 @@
 
 		/* Still need a call? */
 		if (flags != 0) {
-			m_backend.set(sc, flags, false);
+			m_backend.set(m_table, sc, flags, false);
 			it->second.second |= flags;
 		}
 	}
@@ -498,7 +544,7 @@
 		/* Determine if it's a complete removal */
 		bool removal = ((it->second.second) & ~(flags)) == 0;
 
-		m_backend.unset(sc, flags, removal);
+		m_backend.unset(m_table, sc, flags, removal);
 
 		if (removal) {
 			m_table.erase(it);
--- a/C++/tests/Socket/main.cpp	Wed Apr 29 14:04:58 2015 +0200
+++ b/C++/tests/Socket/main.cpp	Wed Apr 29 17:17:35 2015 +0200
@@ -178,28 +178,26 @@
 	bool m_added{false};
 	int m_flags{0};
 
-	inline void set(Socket sc, int flags, bool add) noexcept
+	inline void set(const SocketTable &, Socket sc, int flags, bool add) noexcept
 	{
 		m_callcount ++;
 		m_added = add;
 		m_flags |= flags;
 	}
 
-	inline void unset(Socket, int, bool) noexcept {}
-	SocketStatus wait(const SocketTable &table, int ms) {}
-	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms) {}
+	inline void unset(const SocketTable &, Socket, int, bool) noexcept {}
+	std::vector<SocketStatus> wait(const SocketTable &table, int ms) {}
 };
 
 class TestBackendSetFail {
 public:
-	inline void set(Socket, int, bool)
+	inline void set(const SocketTable &, Socket, int, bool)
 	{
 		throw "fail";
 	}
 
-	inline void unset(Socket, int, bool) noexcept {}
-	SocketStatus wait(const SocketTable &table, int ms) {}
-	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms) {}
+	inline void unset(const SocketTable &, Socket, int, bool) noexcept {}
+	std::vector<SocketStatus> wait(const SocketTable &table, int ms) {}
 };
 
 TEST(ListenerSet, initialAdd)
@@ -279,34 +277,32 @@
 	int m_flags{0};
 	bool m_removal{false};
 
-	inline void set(Socket, int flags, bool) noexcept
+	inline void set(const SocketTable &, Socket, int flags, bool) noexcept
 	{
 		m_isset = true;
 		m_flags |= flags;
 	}
 
-	inline void unset(Socket, int flags, bool remove) noexcept
+	inline void unset(const SocketTable &, Socket, int flags, bool remove) noexcept
 	{
 		m_isunset = true;
 		m_flags &= ~(flags);
 		m_removal = remove;
 	}
 
-	SocketStatus wait(const SocketTable &table, int ms) {}
-	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms) {}
+	std::vector<SocketStatus> wait(const SocketTable &table, int ms) {}
 };
 
 class TestBackendUnsetFail {
 public:
-	inline void set(Socket, int, bool) noexcept {}
+	inline void set(const SocketTable &, Socket, int, bool) noexcept {}
 
-	inline void unset(Socket, int, bool)
+	inline void unset(const SocketTable &, Socket, int, bool)
 	{
 		throw "fail";
 	}
 
-	SocketStatus wait(const SocketTable &table, int ms) {}
-	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms) {}
+	std::vector<SocketStatus> wait(const SocketTable &table, int ms) {}
 };
 
 TEST(ListenerUnsetRemove, unset)