Mercurial > code
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)