changeset 368:6c0015524ca9

Socket: fallback to Select until we update backends
author David Demelier <markand@malikania.fr>
date Wed, 29 Apr 2015 13:51:24 +0200
parents 99484c154d8a
children f3c762579073
files C++/modules/Socket/SocketListener.cpp C++/modules/Socket/SocketListener.h C++/tests/Socket/main.cpp
diffstat 3 files changed, 332 insertions(+), 350 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Socket/SocketListener.cpp	Wed Apr 29 11:17:56 2015 +0200
+++ b/C++/modules/Socket/SocketListener.cpp	Wed Apr 29 13:51:24 2015 +0200
@@ -27,40 +27,9 @@
 
 namespace backend {
 
-void Select::set(Socket s, int flags)
-{
-	if (m_table.count(s.handle()) > 0) {
-		m_table.at(s.handle()).second |= flags;
-	} else {
-		m_table.emplace(s.handle(), std::make_pair(s, flags));
-	}
-}
-
-void Select::unset(Socket s, int flags)
+SocketStatus Select::wait(const SocketTable &table, int ms)
 {
-	if (m_table.count(s.handle()) != 0) {
-		m_table.at(s.handle()).second &= ~(flags);
-
-		// 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());
-}
-
-void Select::clear()
-{
-	m_table.clear();
-}
-
-SocketStatus Select::wait(int ms)
-{
-	std::vector<SocketStatus> result = waitMultiple(ms);
+	std::vector<SocketStatus> result = waitMultiple(table, ms);
 
 	if (result.size() == 0) {
 		throw SocketError(SocketError::System, "select", "No socket found");
@@ -69,7 +38,7 @@
 	return result[0];
 }
 
-std::vector<SocketStatus> Select::waitMultiple(int ms)
+std::vector<SocketStatus> Select::waitMultiple(const SocketTable &table, int ms)
 {
 	timeval maxwait, *towait;
 	fd_set readset;
@@ -80,7 +49,7 @@
 
 	Socket::Handle max = 0;
 
-	for (auto &s : m_table) {
+	for (const auto &s : table) {
 		if (s.second.second & SocketListener::Read) {
 			FD_SET(s.first, &readset);
 		}
@@ -109,7 +78,7 @@
 
 	std::vector<SocketStatus> sockets;
 
-	for (auto &c : m_table) {
+	for (auto &c : table) {
 		if (FD_ISSET(c.first, &readset)) {
 			sockets.push_back(SocketStatus{c.second.first, SocketListener::Read});
 		}
@@ -169,7 +138,7 @@
 	return flags;
 }
 
-void Poll::set(Socket s, int flags)
+void Poll::set(Socket s, int flags, bool)
 {
 	auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) {
 		return pfd.fd == s.handle();
@@ -184,7 +153,7 @@
 	}
 }
 
-void Poll::unset(Socket s, int flags)
+void Poll::unset(Socket s, int flags, bool)
 {
 	for (auto i = m_fds.begin(); i != m_fds.end();) {
 		if (i->fd == s.handle()) {
@@ -202,23 +171,7 @@
 	}
 }
 
-void Poll::remove(Socket s)
-{
-	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()
-{
-	m_fds.clear();
-	m_lookup.clear();
-}
-
-SocketStatus Poll::wait(int ms)
+SocketStatus Poll::wait(const SocketTable &table, int ms)
 {
 	auto result = poll(m_fds.data(), m_fds.size(), ms);
 	if (result == 0) {
@@ -237,7 +190,7 @@
 	throw SocketError(SocketError::System, "select", "No socket found");
 }
 
-std::vector<SocketStatus> Poll::waitMultiple(int ms)
+std::vector<SocketStatus> Poll::waitMultiple(const SocketTable &, int ms)
 {
 	auto result = poll(m_fds.data(), m_fds.size(), ms);
 	if (result == 0) {
--- a/C++/modules/Socket/SocketListener.h	Wed Apr 29 11:17:56 2015 +0200
+++ b/C++/modules/Socket/SocketListener.h	Wed Apr 29 13:51:24 2015 +0200
@@ -68,15 +68,15 @@
 #    define SOCKET_DEFAULT_BACKEND backend::Select
 #  endif
 #elif defined(__linux__)
-   // TODO NOT READY YET
-   //#  define SOCKET_DEFAULT_BACKEND backend::Epoll
-#  define SOCKET_DEFAULT_BACKEND backend::Poll
+//#  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/event.h>
 #  include <sys/time.h>
 
-#  define SOCKET_DEFAULT_BACKEND backend::Kqueue
+//#  define SOCKET_DEFAULT_BACKEND backend::Kqueue
+#  define SOCKET_DEFAULT_BACKEND backend::Select
 #else
 #  define SOCKET_DEFAULT_BACKEND backend::Select
 #endif
@@ -108,6 +108,12 @@
 	int flags;		//!< the flags
 };
 
+/**
+ * Table used in the socket listener to store which sockets have been
+ * set in which directions.
+ */
+using SocketTable = std::map<Socket::Handle, std::pair<Socket, int>>;
+
 namespace backend {
 
 /**
@@ -117,16 +123,25 @@
  * This class is the fallback of any other method, it is not preferred at all for many reasons.
  */
 class Select {
-private:
-	std::map<Socket::Handle, std::pair<Socket, int>> m_table;
+public:
+	/*
+	 * No-op, uses the SocketTable directly.
+	 */
+	inline void set(Socket, int, bool) noexcept
+	{
+		
+	}
 
-public:
-	void set(Socket s, int flags);
-	void unset(Socket s, int flags);
-	void remove(Socket sc);
-	void clear();
-	SocketStatus wait(int ms);
-	std::vector<SocketStatus> waitMultiple(int ms);
+	/*
+	 * No-op, uses the SocketTable directly.
+	 */
+	inline void unset(Socket, int, bool) noexcept
+	{
+		
+	}
+
+	SocketStatus wait(const SocketTable &table, int ms);
+	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms);
 };
 
 #if defined(SOCKET_HAVE_POLL)
@@ -147,22 +162,16 @@
 	int toflags(short &event) const noexcept;
 
 public:
-	void set(Socket s, int flags);
-	void unset(Socket s, int flags);
-	void remove(Socket sc);
-	void clear();
-	SocketStatus wait(int ms);
-	std::vector<SocketStatus> waitMultiple(int ms);
+	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);
 };
 
 #endif
 
 #if defined(SOCKET_HAVE_EPOLL)
 
-class Epoll {
-	// TODO
-};
-
 #endif
 
 #if defined(SOCKET_HAVE_KQUEUE)
@@ -176,7 +185,6 @@
  */
 class Kqueue {
 private:
-	std::map<Socket::Handle, std::pair<Socket, int>> m_table;
 	std::vector<struct kevent> m_result;
 	int m_handle;
 
@@ -191,10 +199,8 @@
 	Kqueue();
 	~Kqueue();
 
-	void set(Socket sc, int flags);
-	void unset(Socket sc, int flags);
-	void remove(Socket sc);
-	void clear();
+	void set(Socket sc, int flags, bool add);
+	void unset(Socket sc, int flags, bool remove);
 	SocketStatus wait(int ms);
 	std::vector<SocketStatus> waitMultiple(int ms);
 };
@@ -224,11 +230,9 @@
 	static const int Read;
 	static const int Write;
 
-	using Map = std::map<Socket, int>;
-
 private:
 	Backend m_backend;
-	Map m_map;
+	SocketTable m_table;
 
 public:
 	inline SocketListenerBase()
@@ -248,13 +252,23 @@
 	}
 
 	/**
-	 * Return an iterator to the beginning.
+	 * Get the backend.
 	 *
-	 * @return the iterator
+	 * @return the backend
 	 */
-	inline auto begin() noexcept
+	inline const Backend &backend() const noexcept
 	{
-		return m_map.begin();
+		return m_backend;
+	}
+
+	/**
+	 * Get the non-modifiable table.
+	 *
+	 * @return the table
+	 */
+	inline const SocketTable &table() const noexcept
+	{
+		return m_table;
 	}
 
 	/**
@@ -264,7 +278,7 @@
 	 */
 	inline auto begin() const noexcept
 	{
-		return m_map.begin();
+		return m_table.begin();
 	}
 
 	/**
@@ -274,17 +288,7 @@
 	 */
 	inline auto cbegin() const noexcept
 	{
-		return m_map.cbegin();
-	}
-
-	/**
-	 * Return an iterator to the end.
-	 *
-	 * @return the iterator
-	 */
-	inline auto end() noexcept
-	{
-		return m_map.end();
+		return m_table.cbegin();
 	}
 
 	/**
@@ -294,7 +298,7 @@
 	 */
 	inline auto end() const noexcept
 	{
-		return m_map.end();
+		return m_table.end();
 	}
 
 	/**
@@ -304,25 +308,22 @@
 	 */
 	inline auto cend() const noexcept
 	{
-		return m_map.cend();
+		return m_table.cend();
 	}
 
 	/**
-	 * Add a socket to the listener.
+	 * Add or update a socket to the listener.
+	 *
+	 * If the socket is already placed with the appropriate flags, the
+	 * function is a no-op.
+	 *
+	 * If incorrect flags are passed, the function does nothing.
 	 *
 	 * @param sc the socket
 	 * @param flags (may be OR'ed)
+	 * @throw SocketError if the backend failed to set
 	 */
-	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);
-		}
-	}
+	void set(Socket sc, int flags);
 
 	/**
 	 * Unset a socket from the listener, only the flags is removed
@@ -335,45 +336,36 @@
 	 * @param flags the flags (may be OR'ed)
 	 * @see remove
 	 */
-	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);
-			}
-		}
-	}
+	void unset(Socket sc, int flags);
 
 	/**
 	 * Remove completely the socket from the listener.
 	 *
+	 * It is a shorthand for unset(sc, SocketListener::Read | SocketListener::Write);
+	 *
 	 * @param sc the socket
 	 */
-	inline void remove(Socket sc) noexcept
+	inline void remove(Socket sc)
 	{
-		m_map.erase(sc);
-		m_backend.remove(sc);
+		unset(sc, Read | Write);
 	}
 
 	/**
 	 * Remove all sockets.
 	 */
-	inline void clear() noexcept
+	inline void clear()
 	{
-		m_map.clear();
-		m_backend.clear();
+		while (!m_table.empty()) {
+			remove(m_table.begin()->second.first);
+		}
 	}
 
 	/**
 	 * Get the number of sockets in the listener.
 	 */
-	unsigned size() const noexcept
+	inline auto size() const noexcept
 	{
-		return m_map.size();
+		return m_table.size();
 	}
 
 	/**
@@ -398,7 +390,7 @@
 	 */
 	inline SocketStatus wait(int timeout = -1)
 	{
-		return m_backend.wait(timeout);
+		return m_backend.wait(m_table, timeout);
 	}
 
 	/**
@@ -412,7 +404,7 @@
 	{
 		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
 
-		return m_backend.waitMultiple(cvt.count());
+		return m_backend.waitMultiple(m_table, cvt.count());
 	}
 
 	/**
@@ -422,10 +414,76 @@
 	 */
 	inline std::vector<SocketStatus> waitMultiple(int timeout = -1)
 	{
-		return m_backend.waitMultiple(timeout);
+		return m_backend.waitMultiple(m_table, timeout);
 	}
 };
 
+template <typename Backend>
+void SocketListenerBase<Backend>::set(Socket sc, int flags)
+{
+	/* Invalid or useless flags */
+	if (flags == 0 || flags > 0x3)
+		return;
+
+	auto it = m_table.find(sc.handle());
+
+	/*
+	 * Do not update the table if the backend failed to add
+	 * or update.
+	 */
+	if (it == m_table.end()) {
+		m_backend.set(sc, flags, true);
+		m_table.emplace(sc.handle(), std::make_pair(sc, flags));
+	} else {
+		if ((flags & Read) && (it->second.second & Read)) {
+			flags &= ~(Read);
+		}
+		if ((flags & Write) && (it->second.second & Write)) {
+			flags &= ~(Write);
+		}
+
+		/* Still need a call? */
+		if (flags != 0) {
+			m_backend.set(sc, flags, false);
+			it->second.second |= flags;
+		}
+	}
+}
+
+template <typename Backend>
+void SocketListenerBase<Backend>::unset(Socket sc, int flags)
+{
+	auto it = m_table.find(sc.handle());
+
+	/* Invalid or useless flags */
+	if (flags == 0 || flags > 0x3 || it == m_table.end())
+		return;
+
+	/*
+	 * Like set, do not update if the socket is already at the appropriate
+	 * state.
+	 */
+	if ((flags & Read) && !(it->second.second & Read)) {
+		flags &= ~(Read);
+	}
+	if ((flags & Write) && !(it->second.second & Write)) {
+		flags &= ~(Write);
+	}
+
+	if (flags != 0) {
+		/* Determine if it's a complete removal */
+		bool removal = ((it->second.second) & ~(flags)) == 0;
+
+		m_backend.unset(sc, flags, removal);
+
+		if (removal) {
+			m_table.erase(it);
+		} else {
+			it->second.second &= ~(flags);
+		}
+	}
+}
+
 /**
  * Helper to use the default.
  */
--- a/C++/tests/Socket/main.cpp	Wed Apr 29 11:17:56 2015 +0200
+++ b/C++/tests/Socket/main.cpp	Wed Apr 29 13:51:24 2015 +0200
@@ -24,12 +24,12 @@
 
 #include <gtest/gtest.h>
 
-#include "Socket.h"
-#include "SocketAddress.h"
-#include "SocketListener.h"
-#include "SocketSsl.h"
-#include "SocketTcp.h"
-#include "SocketUdp.h"
+#include <Socket.h>
+#include <SocketAddress.h>
+#include <SocketListener.h>
+#include <SocketSsl.h>
+#include <SocketTcp.h>
+#include <SocketUdp.h>
 
 using namespace address;
 using namespace std::literals::chrono_literals;
@@ -169,175 +169,225 @@
 }
 
 /* --------------------------------------------------------
- * Listener tests (standard)
+ * Listener: set function
  * -------------------------------------------------------- */
 
-class ListenerTest : public testing::Test {
-protected:
-	SocketListenerBase<backend::Select> m_listener;
-	SocketTcp socket1{AF_INET, 0};
-	SocketUdp socket2{AF_INET, 0};
+class TestBackendSet {
+public:
+	int m_callcount{0};
+	bool m_added{false};
+	int m_flags{0};
+
+	inline void set(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) {}
+};
+
+class TestBackendSetFail {
 public:
-	~ListenerTest()
+	inline void set(Socket, int, bool)
 	{
-		socket1.close();
-		socket2.close();
+		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) {}
 };
 
-TEST_F(ListenerTest, set)
+TEST(ListenerSet, initialAdd)
 {
-	m_listener.set(socket1, SocketListener::Read);
+	SocketListenerBase<TestBackendSet> listener;
+
+	listener.set(Socket(0), SocketListener::Read);
 
-	ASSERT_EQ(1, static_cast<int>(m_listener.size()));
-	ASSERT_EQ(SocketListener::Read, m_listener.begin()->second);
-
-	m_listener.set(socket1, SocketListener::Write);
-
-	ASSERT_EQ(1, static_cast<int>(m_listener.size()));
-	ASSERT_EQ(0x3, m_listener.begin()->second);
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(1, listener.backend().m_callcount);
+	ASSERT_TRUE(listener.backend().m_added);
+	ASSERT_TRUE(listener.backend().m_flags == SocketListener::Read);
+}
 
-	// Fake a re-insert of the same socket
-	m_listener.set(socket1, SocketListener::Write);
-
-	ASSERT_EQ(1, static_cast<int>(m_listener.size()));
-	ASSERT_EQ(0x3, m_listener.begin()->second);
+TEST(ListenerSet, readThenWrite)
+{
+	SocketListenerBase<TestBackendSet> listener;
+	Socket sc(0);
 
-	// Add an other socket now
-	m_listener.set(socket2, SocketListener::Read | SocketListener::Write);
-
-	ASSERT_EQ(2, static_cast<int>(m_listener.size()));
+	listener.set(sc, SocketListener::Read);
+	listener.set(sc, SocketListener::Write);
 
-	for (auto &pair : m_listener) {
-		ASSERT_EQ(0x3, pair.second);
-		ASSERT_TRUE(pair.first == socket1 || pair.first == socket2);
-	}
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(2, listener.backend().m_callcount);
+	ASSERT_FALSE(listener.backend().m_added);
+	ASSERT_TRUE(listener.backend().m_flags == 0x3);
 }
 
-TEST_F(ListenerTest, unset)
+TEST(ListenerSet, allOneShot)
 {
-	m_listener.set(socket1, SocketListener::Read | SocketListener::Write);
-	m_listener.set(socket2, SocketListener::Read | SocketListener::Write);
+	SocketListenerBase<TestBackendSet> listener;
+	Socket sc(0);
+
+	listener.set(sc, SocketListener::Read | SocketListener::Write);
 
-	m_listener.unset(socket1, SocketListener::Read);
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(1, listener.backend().m_callcount);
+	ASSERT_TRUE(listener.backend().m_added);
+	ASSERT_TRUE(listener.backend().m_flags == 0x3);
+}
+
+TEST(ListenerSet, readTwice)
+{
+	SocketListenerBase<TestBackendSet> listener;
+	Socket sc(0);
 
-	ASSERT_EQ(2, static_cast<int>(m_listener.size()));
+	listener.set(sc, SocketListener::Read);
+	listener.set(sc, SocketListener::Read);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(1, listener.backend().m_callcount);
+	ASSERT_TRUE(listener.backend().m_added);
+	ASSERT_TRUE(listener.backend().m_flags == SocketListener::Read);
+}
 
-	// Use a for loop since it can be ordered differently
-	for (auto &pair : m_listener) {
-		if (pair.first == socket1) {
-			ASSERT_EQ(0x2, pair.second);
-		} else if (pair.first == socket2) {
-			ASSERT_EQ(0x3, pair.second);
-		}
+TEST(ListenerSet, failure)
+{
+	SocketListenerBase<TestBackendSetFail> listener;
+
+	try {
+		listener.set(Socket(0), SocketListener::Read);
+		FAIL() << "exception expected";
+	} catch (...) {
 	}
 
-	m_listener.unset(socket1, SocketListener::Write);
-
-	ASSERT_EQ(1, static_cast<int>(m_listener.size()));
-	ASSERT_EQ(0x3, m_listener.begin()->second);
-}
-
-TEST_F(ListenerTest, remove)
-{
-	m_listener.set(socket1, SocketListener::Read | SocketListener::Write);
-	m_listener.set(socket2, SocketListener::Read | SocketListener::Write);
-	m_listener.remove(socket1);
-
-	ASSERT_EQ(1, static_cast<int>(m_listener.size()));
-	ASSERT_EQ(0x3, m_listener.begin()->second);
-}
-
-TEST_F(ListenerTest, clear)
-{
-	m_listener.set(socket1, SocketListener::Read | SocketListener::Write);
-	m_listener.set(socket2, SocketListener::Read | SocketListener::Write);
-	m_listener.clear();
-
-	ASSERT_EQ(0, static_cast<int>(m_listener.size()));
+	ASSERT_EQ(0U, listener.size());
 }
 
 /* --------------------------------------------------------
- * Listener: poll
+ * Listener: unset / remove functions
  * -------------------------------------------------------- */
 
-#if defined(SOCKET_HAVE_POLL)
-
-class ListenerPollTest : public testing::Test {
-protected:
-	SocketListenerBase<backend::Poll> m_listener;
-	SocketTcp m_masterTcp{AF_INET, 0};
-	SocketTcp m_clientTcp{AF_INET, 0};
+class TestBackendUnset {
+public:
+	bool m_isset{false};
+	bool m_isunset{false};
+	int m_flags{0};
+	bool m_removal{false};
 
-	std::thread m_tserver;
-	std::thread m_tclient;
-
-public:
-	ListenerPollTest()
+	inline void set(Socket, int flags, bool) noexcept
 	{
-		m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_masterTcp.bind(Internet("*", 16000, AF_INET));
-		m_masterTcp.listen();
+		m_isset = true;
+		m_flags |= flags;
 	}
 
-	~ListenerPollTest()
+	inline void unset(Socket, int flags, bool remove) noexcept
 	{
-		if (m_tserver.joinable()) {
-			m_tserver.join();
-		}
-		if (m_tclient.joinable()) {
-			m_tclient.join();
-		}
+		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) {}
+};
+
+class TestBackendUnsetFail {
+public:
+	inline void set(Socket, int, bool) noexcept {}
+
+	inline void unset(Socket, int, bool)
+	{
+		throw "fail";
+	}
+
+	SocketStatus wait(const SocketTable &table, int ms) {}
+	std::vector<SocketStatus> waitMultiple(const SocketTable &table, int ms) {}
 };
 
-TEST_F(ListenerPollTest, accept)
+TEST(ListenerUnsetRemove, unset)
 {
-	m_tserver = std::thread([this] () {
-		try {
-			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.wait();
-			m_masterTcp.accept();
-			m_masterTcp.close();
-		} catch (const std::exception &ex) {
-			FAIL() << ex.what();
-		}
-	});
+	SocketListenerBase<TestBackendUnset> listener;
+	Socket sc(0);
+
+	listener.set(sc, SocketListener::Read);
+	listener.unset(sc, SocketListener::Read);
+
+	ASSERT_EQ(0U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == 0);
+	ASSERT_TRUE(listener.backend().m_removal);
+}
 
-	std::this_thread::sleep_for(100ms);
+TEST(ListenerUnsetRemove, unsetOne)
+{
+	SocketListenerBase<TestBackendUnset> listener;
+	Socket sc(0);
 
-	m_tclient = std::thread([this] () {
-		m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET));
-	});
+	listener.set(sc, SocketListener::Read | SocketListener::Write);
+	listener.unset(sc, SocketListener::Read);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == SocketListener::Write);
+	ASSERT_FALSE(listener.backend().m_removal);
 }
 
-TEST_F(ListenerPollTest, recv)
+TEST(ListenerUnsetRemove, unsetAll)
 {
-	m_tserver = std::thread([this] () {
-		try {
-			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.wait();
-
-			auto sc = m_masterTcp.accept();
-
-			ASSERT_EQ("hello", sc.recv(512));
+	SocketListenerBase<TestBackendUnset> listener;
+	Socket sc(0);
 
-			m_masterTcp.close();
-		} catch (const std::exception &ex) {
-			FAIL() << ex.what();
-		}
-	});
+	listener.set(sc, SocketListener::Read | SocketListener::Write);
+	listener.unset(sc, SocketListener::Read);
+	listener.unset(sc, SocketListener::Write);
 
-	std::this_thread::sleep_for(100ms);
-
-	m_tclient = std::thread([this] () {
-		m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET));
-		m_clientTcp.send("hello");
-	});
+	ASSERT_EQ(0U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == 0);
+	ASSERT_TRUE(listener.backend().m_removal);
 }
 
-#endif
+TEST(ListenerUnsetRemove, remove)
+{
+	SocketListenerBase<TestBackendUnset> listener;
+	Socket sc(0);
+
+	listener.set(sc, SocketListener::Read | SocketListener::Write);
+	listener.remove(sc);
+
+	ASSERT_EQ(0U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == 0);
+	ASSERT_TRUE(listener.backend().m_removal);
+}
+
+TEST(ListenerUnsetRemove, failure)
+{
+	SocketListenerBase<TestBackendUnsetFail> listener;
+	Socket sc(0);
+
+	listener.set(sc, SocketListener::Read | SocketListener::Write);
+
+	try {
+		listener.remove(sc);
+		FAIL() << "exception expected";
+	} catch (...) {
+	}
+
+	/* If fail, kept into the table */
+	ASSERT_EQ(1U, listener.size());
+}
+
+#if 0
 
 /* --------------------------------------------------------
  * Listener: select
@@ -416,86 +466,7 @@
 	});
 }
 
-/* --------------------------------------------------------
- * Listener: kqueue
- * -------------------------------------------------------- */
-
-#if defined(SOCKET_HAVE_KQUEUE)
-
-class ListenerKqueueTest : public testing::Test {
-protected:
-	SocketListenerBase<backend::Kqueue> m_listener;
-	SocketTcp m_masterTcp{AF_INET, 0};
-	SocketTcp m_clientTcp{AF_INET, 0};
-
-	std::thread m_tserver;
-	std::thread m_tclient;
-
-public:
-	ListenerKqueueTest()
-	{
-		m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_masterTcp.bind(Internet("*", 16000, AF_INET));
-		m_masterTcp.listen();
-	}
-
-	~ListenerKqueueTest()
-	{
-		if (m_tserver.joinable()) {
-			m_tserver.join();
-		}
-		if (m_tclient.joinable()) {
-			m_tclient.join();
-		}
-	}
-};
-
-TEST_F(ListenerKqueueTest, accept)
-{
-	m_tserver = std::thread([this] () {
-		try {
-			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.wait();
-			m_masterTcp.accept();
-			m_masterTcp.close();
-		} catch (const std::exception &ex) {
-			FAIL() << ex.what();
-		}
-	});
-
-	std::this_thread::sleep_for(100ms);
-
-	m_tclient = std::thread([this] () {
-		m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET));
-	});
-}
-
-TEST_F(ListenerKqueueTest, recv)
-{
-	m_tserver = std::thread([this] () {
-		try {
-			m_listener.set(m_masterTcp, SocketListener::Read);
-			m_listener.wait();
-
-			auto sc = m_masterTcp.accept();
-
-			ASSERT_EQ("hello", sc.recv(512));
-
-			m_masterTcp.close();
-		} catch (const std::exception &ex) {
-			FAIL() << ex.what();
-		}
-	});
-
-	std::this_thread::sleep_for(100ms);
-
-	m_tclient = std::thread([this] () {
-		m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET));
-		m_clientTcp.send("hello");
-	});
-}
-
-#endif // !SOCKET_HAVE_KQUEUE
+#endif
 
 /* --------------------------------------------------------
  * Non-blocking connect