changeset 346:d235553e47a9

Socket: - SocketListener is now implemented as a template and use the backend as the template parameter. No more virtual implementation, - Improve backend detection and set the default backend implementation automatically, - Rename Socket::init to Socket::initialize for non-Windows systems, - Rename SocketListener::(select|selectMultiple) to wait|waitMultiple - Coming up soon: kqueue and epoll backends.
author David Demelier <markand@malikania.fr>
date Thu, 02 Apr 2015 17:32:51 +0200
parents c293dbe181c0
children b8d6b7f0bec4
files C++/modules/Socket/Socket.h C++/modules/Socket/SocketListener.cpp C++/modules/Socket/SocketListener.h C++/modules/Socket/SocketSsl.cpp C++/modules/Socket/SocketTcp.cpp C++/tests/Socket/main.cpp
diffstat 6 files changed, 368 insertions(+), 377 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Socket/Socket.h	Tue Mar 31 09:57:37 2015 +0200
+++ b/C++/modules/Socket/Socket.h	Thu Apr 02 17:32:51 2015 +0200
@@ -238,7 +238,7 @@
 	/**
 	 * no-op.
 	 */
-	static inline void init() noexcept {}
+	static inline void initialize() noexcept {}
 
 	/**
 	 * no-op.
--- a/C++/modules/Socket/SocketListener.cpp	Tue Mar 31 09:57:37 2015 +0200
+++ b/C++/modules/Socket/SocketListener.cpp	Thu Apr 02 17:32:51 2015 +0200
@@ -28,116 +28,102 @@
  * Select implementation
  * -------------------------------------------------------- */
 
-namespace {
+namespace backend {
+
+void Select::set(Socket &s, int direction)
+{
+	if (m_table.count(s.handle()) > 0) {
+		m_table.at(s.handle()).second |= direction;
+	} else {
+		m_table.insert({s.handle(), {s, direction}});
+	}
+
+}
+
+void Select::unset(Socket &s, int direction)
+{
+	if (m_table.count(s.handle()) != 0) {
+		m_table.at(s.handle()).second &= ~(direction);
+
+		// If no read, no write is requested, remove it
+		if (m_table.at(s.handle()).second == 0) {
+			m_table.erase(s.handle());
+		}
+	}
+}
+
+void Select::remove(Socket &sc)
+{
+	m_table.erase(sc.handle());
+}
 
-/**
- * @class SelectMethod
- * @brief Implements select(2)
- *
- * This class is the fallback of any other method, it is not preferred at all for many reasons.
- */
-class SelectMethod final : public SocketListenerInterface {
-private:
-	std::map<Socket::Handle, std::pair<std::reference_wrapper<Socket>, int>> m_table;
+void Select::clear()
+{
+	m_table.clear();
+}
+
+SocketStatus Select::wait(int ms)
+{
+	auto result = waitMultiple(ms);
+
+	if (result.size() == 0) {
+		throw SocketError(SocketError::System, "select", "No socket found");
+	}
+
+	return result[0];
+}
 
-public:
-	void set(Socket &s, int direction) override
-	{
-		if (m_table.count(s.handle()) > 0) {
-			m_table.at(s.handle()).second |= direction;
-		} else {
-			m_table.insert({s.handle(), {s, direction}});
+std::vector<SocketStatus> Select::waitMultiple(int ms)
+{
+	timeval maxwait, *towait;
+	fd_set readset;
+	fd_set writeset;
+
+	FD_ZERO(&readset);
+	FD_ZERO(&writeset);
+
+	Socket::Handle max = 0;
+
+	for (auto &s : m_table) {
+		if (s.second.second & SocketListener::Read) {
+			FD_SET(s.first, &readset);
+		}
+		if (s.second.second & SocketListener::Write) {
+			FD_SET(s.first, &writeset);
 		}
 
-	}
-
-	void unset(Socket &s, int direction) override
-	{
-		if (m_table.count(s.handle()) != 0) {
-			m_table.at(s.handle()).second &= ~(direction);
-
-			// If no read, no write is requested, remove it
-			if (m_table.at(s.handle()).second == 0) {
-				m_table.erase(s.handle());
-			}
+		if (s.first > max) {
+			max = s.first;
 		}
 	}
 
-	void remove(Socket &sc) override
-	{
-		m_table.erase(sc.handle());
-	}
+	maxwait.tv_sec = 0;
+	maxwait.tv_usec = ms * 1000;
 
-	void clear() override
-	{
-		m_table.clear();
-	}
+	// Set to nullptr for infinite timeout.
+	towait = (ms < 0) ? nullptr : &maxwait;
 
-	SocketStatus select(int ms) override
-	{
-		auto result = selectMultiple(ms);
-
-		if (result.size() == 0) {
-			throw SocketError(SocketError::System, "select", "No socket found");
-		}
-
-		return result[0];
+	auto error = ::select(max + 1, &readset, &writeset, nullptr, towait);
+	if (error == Socket::Error) {
+		throw SocketError(SocketError::System, "select");
+	}
+	if (error == 0) {
+		throw SocketError(SocketError::Timeout, "select", "Timeout while listening");
 	}
 
-	std::vector<SocketStatus> selectMultiple(int ms) override
-	{
-		timeval maxwait, *towait;
-		fd_set readset;
-		fd_set writeset;
-
-		FD_ZERO(&readset);
-		FD_ZERO(&writeset);
-
-		Socket::Handle max = 0;
-
-		for (auto &s : m_table) {
-			if (s.second.second & SocketListener::Read) {
-				FD_SET(s.first, &readset);
-			}
-			if (s.second.second & SocketListener::Write) {
-				FD_SET(s.first, &writeset);
-			}
-
-			if (s.first > max) {
-				max = s.first;
-			}
-		}
-
-		maxwait.tv_sec = 0;
-		maxwait.tv_usec = ms * 1000;
+	std::vector<SocketStatus> sockets;
 
-		// Set to nullptr for infinite timeout.
-		towait = (ms < 0) ? nullptr : &maxwait;
-
-		auto error = ::select(max + 1, &readset, &writeset, nullptr, towait);
-		if (error == Socket::Error) {
-			throw SocketError(SocketError::System, "select");
+	for (auto &c : m_table) {
+		if (FD_ISSET(c.first, &readset)) {
+			sockets.push_back({ c.second.first, SocketListener::Read });
 		}
-		if (error == 0) {
-			throw SocketError(SocketError::Timeout, "select", "Timeout while listening");
+		if (FD_ISSET(c.first, &writeset)) {
+			sockets.push_back({ c.second.first, SocketListener::Write });
 		}
-
-		std::vector<SocketStatus> sockets;
+	}
 
-		for (auto &c : m_table) {
-			if (FD_ISSET(c.first, &readset)) {
-				sockets.push_back({ c.second.first, SocketListener::Read });
-			}
-			if (FD_ISSET(c.first, &writeset)) {
-				sockets.push_back({ c.second.first, SocketListener::Write });
-			}
-		}
-
-		return sockets;
-	}
-};
-
-} // !namespace
+	return sockets;
+}
 
 /* --------------------------------------------------------
  * Poll implementation
@@ -146,193 +132,130 @@
 #if defined(SOCKET_HAVE_POLL)
 
 #if defined(_WIN32)
-#  include <Winsock2.h>
 #  define poll WSAPoll
-#else
-#  include <poll.h>
 #endif
 
-namespace {
-
-class PollMethod final : public SocketListenerInterface {
-private:
-	std::vector<pollfd> m_fds;
-	std::map<Socket::Handle, std::reference_wrapper<Socket>> m_lookup;
+short Poll::topoll(int direction) const noexcept
+{
+	short result(0);
 
-	inline short topoll(int direction)
-	{
-		short result(0);
-
-		if (direction & SocketListener::Read) {
-			result |= POLLIN;
-		}
-		if (direction & SocketListener::Write) {
-			result |= POLLOUT;
-		}
-
-		return result;
+	if (direction & SocketListener::Read) {
+		result |= POLLIN;
+	}
+	if (direction & SocketListener::Write) {
+		result |= POLLOUT;
 	}
 
-	inline int todirection(short event)
-	{
-		int direction{};
+	return result;
+}
+
+int Poll::todirection(short event) const noexcept
+{
+	int direction{};
 
-		/*
-		 * Poll implementations mark the socket differently regarding
-		 * the disconnection of a socket.
-		 *
-		 * At least, even if POLLHUP or POLLIN is set, recv() always
-		 * return 0 so we mark the socket as readable.
-		 */
-		if ((event & POLLIN) || (event & POLLHUP)) {
-			direction |= SocketListener::Read;
-		}
-		if (event & POLLOUT) {
-			direction |= SocketListener::Write;
-		}
-
-		return direction;
+	/*
+	 * Poll implementations mark the socket differently regarding
+	 * the disconnection of a socket.
+	 *
+	 * At least, even if POLLHUP or POLLIN is set, recv() always
+	 * return 0 so we mark the socket as readable.
+	 */
+	if ((event & POLLIN) || (event & POLLHUP)) {
+		direction |= SocketListener::Read;
+	}
+	if (event & POLLOUT) {
+		direction |= SocketListener::Write;
 	}
 
-public:
-	void set(Socket &s, int direction) override
-	{
-		auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); });
+	return direction;
+}
+
+void Poll::set(Socket &s, int direction) override
+{
+	auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); });
 
-		// If found, add the new direction, otherwise add a new socket
-		if (it != m_fds.end()) {
-			it->events |= topoll(direction);
-		} else {
-			m_lookup.insert({s.handle(), s});
-			m_fds.push_back({ s.handle(), topoll(direction), 0 });
-		}
+	// If found, add the new direction, otherwise add a new socket
+	if (it != m_fds.end()) {
+		it->events |= topoll(direction);
+	} else {
+		m_lookup.insert({s.handle(), s});
+		m_fds.push_back({ s.handle(), topoll(direction), 0 });
 	}
-
-	void unset(Socket &s, int direction) override
-	{
-		for (auto i = m_fds.begin(); i != m_fds.end();) {
-			if (i->fd == s.handle()) {
-				i->events &= ~(topoll(direction));
+}
 
-				if (i->events == 0) {
-					m_lookup.erase(i->fd);
-					i = m_fds.erase(i);
-				} else {
-					++i;
-				}
+void Poll::unset(Socket &s, int direction) override
+{
+	for (auto i = m_fds.begin(); i != m_fds.end();) {
+		if (i->fd == s.handle()) {
+			i->events &= ~(topoll(direction));
+
+			if (i->events == 0) {
+				m_lookup.erase(i->fd);
+				i = m_fds.erase(i);
 			} else {
 				++i;
 			}
+		} else {
+			++i;
 		}
 	}
+}
+
+void Poll::remove(Socket &s) override
+{
+	auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); });
+
+	if (it != m_fds.end()) {
+		m_fds.erase(it);
+		m_lookup.erase(s.handle());
+	}
+}
+
+void Poll::clear() override
+{
+	m_fds.clear();
+	m_lookup.clear();
+}
+
+SocketStatus Poll::wait(int ms) override
+{
+	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");
+	}
 
-	void remove(Socket &s) override
-	{
-		auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); });
-
-		if (it != m_fds.end()) {
-			m_fds.erase(it);
-			m_lookup.erase(s.handle());
+	for (auto &fd : m_fds) {
+		if (fd.revents != 0) {
+			return { m_lookup.at(fd.fd), todirection(fd.revents) };
 		}
 	}
 
-	void clear() override
-	{
-		m_fds.clear();
-		m_lookup.clear();
+	throw SocketError(SocketError::System, "select", "No socket found");
+}
+
+std::vector<SocketStatus> Poll::waitMultiple(int ms) override
+{
+	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");
 	}
 
-	SocketStatus select(int ms) override
-	{
-		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");
+	std::vector<SocketStatus> sockets;
+	for (auto &fd : m_fds) {
+		if (fd.revents != 0) {
+			sockets.push_back({ m_lookup.at(fd.fd), todirection(fd.revents) });
 		}
-
-		for (auto &fd : m_fds) {
-			if (fd.revents != 0) {
-				return { m_lookup.at(fd.fd), todirection(fd.revents) };
-			}
-		}
-
-		throw SocketError(SocketError::System, "select", "No socket found");
 	}
 
-	std::vector<SocketStatus> selectMultiple(int ms) override
-	{
-		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");
-		}
-
-		std::vector<SocketStatus> sockets;
-		for (auto &fd : m_fds) {
-			if (fd.revents != 0) {
-				sockets.push_back({ m_lookup.at(fd.fd), todirection(fd.revents) });
-			}
-		}
-
-		return sockets;
-	}
-};
-
-} // !namespace
+	return sockets;
+}
 
 #endif // !_SOCKET_HAVE_POLL
 
-/* --------------------------------------------------------
- * SocketListener
- * -------------------------------------------------------- */
-
-const int SocketListener::Read{1 << 0};
-const int SocketListener::Write{1 << 1};
-
-SocketListener::SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list)
-	: SocketListener()
-{
-	for (const auto &p : list) {
-		set(p.first, p.second);
-	}
-}
-
-SocketListener::SocketListener(SocketMethod method)
-{
-#if defined(SOCKET_HAVE_POLL)
-	if (method == SocketMethod::Poll)
-		m_interface = std::make_unique<PollMethod>();
-	else
-#endif
-		m_interface = std::make_unique<SelectMethod>();
-
-	(void)method;
-}
-
-void SocketListener::set(Socket &sc, int flags)
-{
-	if (m_map.count(sc) > 0) {
-		m_map[sc] |= flags;
-		m_interface->set(sc, flags);
-	} else {
-		m_map.insert({sc, flags});
-		m_interface->set(sc, flags);
-	}
-}
-
-void SocketListener::unset(Socket &sc, int flags) noexcept
-{
-	if (m_map.count(sc) > 0) {
-		m_map[sc] &= ~(flags);
-		m_interface->unset(sc, flags);
-
-		// No more flags, remove it
-		if (m_map[sc] == 0) {
-			m_map.erase(sc);
-		}
-	}
-}
+} // !backend
\ No newline at end of file
--- a/C++/modules/Socket/SocketListener.h	Tue Mar 31 09:57:37 2015 +0200
+++ b/C++/modules/Socket/SocketListener.h	Thu Apr 02 17:32:51 2015 +0200
@@ -19,6 +19,62 @@
 #ifndef _SOCKET_LISTENER_NG_H_
 #define _SOCKET_LISTENER_NG_H_
 
+/**
+ * Feature detection, multiple implementations may be avaible, for example,
+ * Linux has poll, select and epoll.
+ *
+ * We assume that select(2) is always available.
+ *
+ * Of course, you can set the variables yourself if you test it with your
+ * build system.
+ *
+ * SOCKET_HAVE_POLL	- Defined on all BSD, Linux. Also defined on Windows
+ *			  if _WIN32_WINNT is set to 0x0600 or greater. 
+ * SOCKET_HAVE_KQUEUE	- Defined on all BSD and Apple.
+ * SOCKET_HAVE_EPOLL	- Defined on Linux only.
+ */
+#if defined(_WIN32)
+#  if _WIN32_WINNT >= 0x0600
+#    define SOCKET_HAVE_POLL
+#  endif
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#  define SOCKET_HAVE_KQUEUE
+#  define SOCKET_HAVE_POLL
+#elif defined(__linux__)
+#  define SOCKET_HAVE_EPOLL
+#  define SOCKET_HAVE_POLL
+#endif
+
+/**
+ * This sets the default backend to use depending on the system. The following
+ * table summaries.
+ *
+ * The preference priority is ordered from left to right.
+ *
+ * +---------------+-------------------------+
+ * | System        | Backend                 |
+ * +---------------+-------------------------+
+ * | Linux         | epoll(2)                |
+ * | *BSD          | kqueue(2)               |
+ * | Windows       | poll(2), select(2)      |
+ * | Mac OS X      | kqueue(2)               |
+ * +---------------+-------------------------+
+ */
+
+#if defined(_WIN32)
+#  if defined(SOCKET_HAVE_POLL)
+#    define SOCKET_DEFAULT_BACKEND backend::Poll
+#  else
+#    define SOCKET_DEFAULT_BACKEND backend::Select
+#  endif
+#elif defined(__linux__)
+#  define SOCKET_DEFAULT_BACKEND backend::Epoll
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#  define SOCKET_DEFAULT_BACKEND backend::Kqueue
+#else
+#  define SOCKET_DEFAULT_BACKEND backend::Select
+#endif
+
 #include <chrono>
 #include <functional>
 #include <initializer_list>
@@ -29,101 +85,85 @@
 
 #include "Socket.h"
 
-#if defined(_WIN32)
-#  if _WIN32_WINNT >= 0x0600
-#    define SOCKET_HAVE_POLL
-#  endif
-#else
-#  define SOCKET_HAVE_POLL
+#if defined(SOCKET_HAVE_POLL)
+#  include <poll.h>
 #endif
 
 /**
- * @enum SocketMethod
- * @brief The SocketMethod enum
- *
- * Select the method of polling. It is only a preferred method, for example if you
- * request for poll but it is not available, select will be used.
- */
-enum class SocketMethod {
-	Select,				//!< select(2) method, fallback
-	Poll				//!< poll(2), everywhere possible
-};
-
-/**
  * @struct SocketStatus
  * @brief The SocketStatus struct
  *
  * Result of a select call, returns the first ready socket found with its
- * direction.
+ * flags.
  */
 class SocketStatus {
 public:
 	Socket	&socket;		//!< which socket is ready
-	int	 direction;		//!< the direction
+	int	 flags;			//!< the flags
 };
 
+namespace backend {
+
 /**
- * @class SocketListenerInterface
- * @brief Implement the polling method
+ * @class Select
+ * @brief Implements select(2)
+ *
+ * This class is the fallback of any other method, it is not preferred at all for many reasons.
  */
-class SocketListenerInterface {
-public:
-	/**
-	 * Default destructor.
-	 */
-	virtual ~SocketListenerInterface() = default;
-
-	/**
-	 * Add a socket with a specified direction.
-	 *
-	 * @param s the socket
-	 * @param direction the direction
-	 */
-	virtual void set(Socket &sc, int direction) = 0;
-
-	/**
-	 * Remove a socket with a specified direction.
-	 *
-	 * @param s the socket
-	 * @param direction the direction
-	 */
-	virtual void unset(Socket &sc, int direction) = 0;
+class Select {
+private:
+	std::map<Socket::Handle, std::pair<std::reference_wrapper<Socket>, int>> m_table;
 
-	/**
-	 * Remove completely a socket.
-	 *
-	 * @param sc the socket to remove
-	 */
-	virtual void remove(Socket &sc) = 0;
-
-	/**
-	 * Remove all sockets.
-	 */
-	virtual void clear() = 0;
-
-	/**
-	 * Select one socket.
-	 *
-	 * @param ms the number of milliseconds to wait, -1 means forever
-	 * @return the socket status
-	 * @throw error::Failure on failure
-	 * @throw error::Timeout on timeout
-	 */
-	virtual SocketStatus select(int ms) = 0;
-
-	/**
-	 * Select many sockets.
-	 *
-	 * @param ms the number of milliseconds to wait, -1 means forever
-	 * @return a vector of ready sockets
-	 * @throw error::Failure on failure
-	 * @throw error::Timeout on timeout
-	 */
-	virtual std::vector<SocketStatus> selectMultiple(int ms) = 0;
+public:
+	void set(Socket &s, int direction);
+	void unset(Socket &s, int direction);
+	void remove(Socket &sc);
+	void clear();
+	SocketStatus wait(int ms);
+	std::vector<SocketStatus> waitMultiple(int ms);
 };
 
+#if defined(SOCKET_HAVE_POLL)
+
+class Poll {
+private:
+	std::vector<pollfd> m_fds;
+	std::map<Socket::Handle, std::reference_wrapper<Socket>> m_lookup;
+
+	short topoll(int direction) const noexcept;
+	int todirection(short event) const noexcept;
+
+public:
+	void set(Socket &s, int direction);
+	void unset(Socket &s, int direction);
+	void remove(Socket &sc);
+	void clear();
+	SocketStatus wait(int ms);
+	std::vector<SocketStatus> waitMultiple(int ms);
+};
+
+#endif
+
+#if defined(SOCKET_HAVE_EPOLL)
+
+class Epoll {
+	// TODO
+};
+
+#endif
+
+#if defined(SOCKET_HAVE_KQUEUE)
+
+class Kqueue {
+	// TODO
+};
+
+#endif
+
+} // !backend
+
 /**
- * @class SocketListener
+ * @class SocketListenerBase
  * @brief Synchronous multiplexing
  *
  * Convenient wrapper around the select() system call.
@@ -137,25 +177,20 @@
  * own them so you must take care that sockets are still alive until the
  * SocketListener is destroyed.
  */
-class SocketListener final {
+template <typename Backend = SOCKET_DEFAULT_BACKEND>
+class SocketListenerBase final {
 public:
-#if defined(SOCKET_HAVE_POLL)
-	static constexpr const SocketMethod PreferredMethod = SocketMethod::Poll;
-#else
-	static constexpr const SocketMethod PreferredMethod = SocketMethod::Select;
-#endif
-
 	static const int Read;
 	static const int Write;
 
 	using Map = std::map<std::reference_wrapper<Socket>, int>;
-	using Iface = std::unique_ptr<SocketListenerInterface>;
 
 private:
+	Backend m_backend;
 	Map m_map;
-	Iface m_interface;
 
 public:
+#if 0
 	/**
 	 * Move constructor.
 	 *
@@ -170,20 +205,22 @@
 	 * @return this
 	 */
 	SocketListener &operator=(SocketListener &&other) = default;
-
-	/**
-	 * Create a socket listener.
-	 *
-	 * @param method the preferred method
-	 */
-	SocketListener(SocketMethod method = PreferredMethod);
+#endif
+	inline SocketListenerBase()
+	{
+	}
 
 	/**
 	 * Create a listener from a list of sockets.
 	 *
 	 * @param list the list
 	 */
-	SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list);
+	inline SocketListenerBase(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list)
+	{
+		for (const auto &p : list) {
+			set(p.first, p.second);
+		}
+	}
 
 	/**
 	 * Return an iterator to the beginning.
@@ -249,9 +286,18 @@
 	 * Add a socket to the listener.
 	 *
 	 * @param sc the socket
-	 * @param direction (may be OR'ed)
+	 * @param flags (may be OR'ed)
 	 */
-	void set(Socket &sc, int direction);
+	void set(Socket &sc, int flags)
+	{
+		if (m_map.count(sc) > 0) {
+			m_map[sc] |= flags;
+			m_backend.set(sc, flags);
+		} else {
+			m_map.insert({sc, flags});
+			m_backend.set(sc, flags);
+		}
+	}
 
 	/**
 	 * Unset a socket from the listener, only the direction is removed
@@ -261,10 +307,21 @@
 	 * unsetting the write direction will keep the socket for reading.
 	 *
 	 * @param sc the socket
-	 * @param direction the direction (may be OR'ed)
+	 * @param flags the flags (may be OR'ed)
 	 * @see remove
 	 */
-	void unset(Socket &sc, int direction) noexcept;
+	void unset(Socket &sc, int flags) noexcept
+	{
+		if (m_map.count(sc) > 0) {
+			m_map[sc] &= ~(flags);
+			m_backend.unset(sc, flags);
+
+			// No more flags, remove it
+			if (m_map[sc] == 0) {
+				m_map.erase(sc);
+			}
+		}
+	}
 
 	/**
 	 * Remove completely the socket from the listener.
@@ -274,7 +331,7 @@
 	inline void remove(Socket &sc) noexcept
 	{
 		m_map.erase(sc);
-		m_interface->remove(sc);
+		m_backend.remove(sc);
 	}
 
 	/**
@@ -283,7 +340,7 @@
 	inline void clear() noexcept
 	{
 		m_map.clear();
-		m_interface->clear();
+		m_backend.clear();
 	}
 
 	/**
@@ -301,11 +358,11 @@
 	 * @return the socket ready
 	 */
 	template <typename Rep, typename Ratio>
-	inline SocketStatus select(const std::chrono::duration<Rep, Ratio> &duration)
+	inline SocketStatus wait(const std::chrono::duration<Rep, Ratio> &duration)
 	{
 		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
 
-		return m_interface->select(cvt.count());
+		return m_backend.wait(cvt.count());
 	}
 
 	/**
@@ -314,9 +371,9 @@
 	 * @param timeout the optional timeout in milliseconds
 	 * @return the socket ready
 	 */
-	inline SocketStatus select(int timeout = -1)
+	inline SocketStatus wait(int timeout = -1)
 	{
-		return m_interface->select(timeout);
+		return m_backend.wait(timeout);
 	}
 
 	/**
@@ -326,11 +383,11 @@
 	 * @return the socket ready
 	 */
 	template <typename Rep, typename Ratio>
-	inline std::vector<SocketStatus> selectMultiple(const std::chrono::duration<Rep, Ratio> &duration)
+	inline std::vector<SocketStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration)
 	{
 		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
 
-		return m_interface->selectMultiple(cvt.count());
+		return m_backend.waitMultiple(cvt.count());
 	}
 
 	/**
@@ -338,10 +395,21 @@
 	 *
 	 * @return the socket ready
 	 */
-	inline std::vector<SocketStatus> selectMultiple(int timeout = -1)
+	inline std::vector<SocketStatus> waitMultiple(int timeout = -1)
 	{
-		return m_interface->selectMultiple(timeout);
+		return m_backend.waitMultiple(timeout);
 	}
 };
 
+/**
+ * Helper to use the default.
+ */
+using SocketListener = SocketListenerBase<>;
+
+template <typename Backend>
+const int SocketListenerBase<Backend>::Read{1 << 0};
+
+template <typename Backend>
+const int SocketListenerBase<Backend>::Write{1 << 1};
+
 #endif // !_SOCKET_LISTENER_NG_H_
--- a/C++/modules/Socket/SocketSsl.cpp	Tue Mar 31 09:57:37 2015 +0200
+++ b/C++/modules/Socket/SocketSsl.cpp	Thu Apr 02 17:32:51 2015 +0200
@@ -119,7 +119,7 @@
 		if (ex.code() == SocketError::WouldBlockRead || ex.code() == SocketError::WouldBlockWrite) {
 			SocketListener listener{{*this, toDirection(ex.code())}};
 
-			listener.select(timeout);
+			listener.wait(timeout);
 
 			// Second try
 			connect(address);
@@ -195,7 +195,7 @@
 {
 	SocketListener listener{{*this, SocketListener::Read}};
 
-	listener.select(timeout);
+	listener.wait(timeout);
 
 	return recv(data, len);
 }
@@ -223,7 +223,7 @@
 {
 	SocketListener listener{{*this, SocketListener::Write}};
 
-	listener.select(timeout);
+	listener.wait(timeout);
 
 	return send(data, len);
 }
--- a/C++/modules/Socket/SocketTcp.cpp	Tue Mar 31 09:57:37 2015 +0200
+++ b/C++/modules/Socket/SocketTcp.cpp	Thu Apr 02 17:32:51 2015 +0200
@@ -133,7 +133,7 @@
 		if (ex.code() == SocketError::WouldBlockWrite) {
 			SocketListener listener{{*this, SocketListener::Write}};
 
-			listener.select(timeout);
+			listener.wait(timeout);
 
 			// Socket is writable? Check if there is an error
 
@@ -161,7 +161,7 @@
 {
 	SocketListener listener{{*this, SocketListener::Read}};
 
-	listener.select(timeout);
+	listener.wait(timeout);
 
 	return accept(info);
 }
@@ -198,7 +198,7 @@
 {
 	SocketListener listener{{*this, SocketListener::Read}};
 
-	listener.select(timeout);
+	listener.wait(timeout);
 
 	return recv(data, length);
 }
@@ -233,7 +233,7 @@
 {
 	SocketListener listener{{*this, SocketListener::Write}};
 
-	listener.select(timeout);
+	listener.wait(timeout);
 
 	return send(data, length);
 }
--- a/C++/tests/Socket/main.cpp	Tue Mar 31 09:57:37 2015 +0200
+++ b/C++/tests/Socket/main.cpp	Thu Apr 02 17:32:51 2015 +0200
@@ -174,7 +174,7 @@
 
 class ListenerTest : public testing::Test {
 protected:
-	SocketListener m_listener;
+	SocketListenerBase<backend::Select> m_listener;
 	SocketTcp socket1{AF_INET, 0};
 	SocketUdp socket2{AF_INET, 0};
 
@@ -266,7 +266,7 @@
 
 class ListenerPollTest : public testing::Test {
 protected:
-	SocketListener m_listener{SocketMethod::Poll};
+	SocketListener<backend::Poll> m_listener;
 	SocketTcp m_masterTcp{AF_INET, 0};
 	SocketTcp m_clientTcp{AF_INET, 0};
 
@@ -317,7 +317,7 @@
 	m_tserver = std::thread([this] () {
 		try {
 			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.select();
+			m_listener.wait();
 
 			auto sc = m_masterTcp.accept();
 
@@ -345,7 +345,7 @@
 
 class ListenerSelectTest : public testing::Test {
 protected:
-	SocketListener m_listener{SocketMethod::Select};
+	SocketListenerBase<backend::Select> m_listener;
 	SocketTcp m_masterTcp{AF_INET, 0};
 	SocketTcp m_clientTcp{AF_INET, 0};
 
@@ -376,7 +376,7 @@
 	m_tserver = std::thread([this] () {
 		try {
 			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.select();
+			m_listener.wait();
 			m_masterTcp.accept();
 			m_masterTcp.close();
 		} catch (const std::exception &ex) {
@@ -396,7 +396,7 @@
 	m_tserver = std::thread([this] () {
 		try {
 			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.select();
+			m_listener.wait();
 
 			auto sc = m_masterTcp.accept();