From a13b17573e1456653b267140e613a1cfebc69fa3 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 26 Sep 2016 18:34:06 +0200 Subject: [PATCH] Network/SocketPoller: Switch to epoll implementation on Linux Former-commit-id: 1a4b998bff35b5aac411b053fe3dee48f1f6985c [formerly b7a50753347b629f708f21d85efc9e76e4b1bfc6] [formerly 7d59f9ff3d2173657cc5873209753fe64b59e2f2 [formerly 4c38f94a4a366ed290e605870e6f3c87e6decd7f]] Former-commit-id: af5cc261c162ca3eebe5885acd5e2adfbd817984 [formerly 26e7b701e8dcafb7fb9c3537107729b2d0bfe354] Former-commit-id: 00bd2c62ecdb5c493c4ec117dd2033d272f7143a --- build/scripts/modules/network.lua | 5 + src/Nazara/Network/Linux/SocketPollerImpl.cpp | 99 +++++++++++++++++++ src/Nazara/Network/Linux/SocketPollerImpl.hpp | 43 ++++++++ 3 files changed, 147 insertions(+) create mode 100644 src/Nazara/Network/Linux/SocketPollerImpl.cpp create mode 100644 src/Nazara/Network/Linux/SocketPollerImpl.hpp diff --git a/build/scripts/modules/network.lua b/build/scripts/modules/network.lua index 8cf8b068a..987c6a822 100644 --- a/build/scripts/modules/network.lua +++ b/build/scripts/modules/network.lua @@ -14,6 +14,11 @@ MODULE.OsFiles.Posix = { "../src/Nazara/Network/Posix/**.cpp" } +MODULE.OsFiles.Linux = { + "../src/Nazara/Network/Linux/**.hpp", + "../src/Nazara/Network/Linux/**.cpp" +} + MODULE.OsLibraries.Windows = { "ws2_32" } diff --git a/src/Nazara/Network/Linux/SocketPollerImpl.cpp b/src/Nazara/Network/Linux/SocketPollerImpl.cpp new file mode 100644 index 000000000..9a0806d8a --- /dev/null +++ b/src/Nazara/Network/Linux/SocketPollerImpl.cpp @@ -0,0 +1,99 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + SocketPollerImpl::SocketPollerImpl() + { + m_handle = epoll_create1(0); + } + + SocketPollerImpl::~SocketPollerImpl() + { + close(m_handle); + } + + void SocketPollerImpl::Clear() + { + m_activeSockets.clear(); + m_sockets.clear(); + } + + bool SocketPollerImpl::IsReady(SocketHandle socket) const + { + return m_activeSockets.count(socket) != 0; + } + + bool SocketPollerImpl::IsRegistered(SocketHandle socket) const + { + return m_sockets.count(socket) != 0; + } + + bool SocketPollerImpl::RegisterSocket(SocketHandle socket) + { + NazaraAssert(!IsRegistered(socket), "Socket is already registered"); + + epoll_event event; + event.events = EPOLLIN; + event.data.fd = socket; + + if (epoll_ctl(m_handle, EPOLL_CTL_ADD, socket, &event) != 0) + { + NazaraError("Failed to add socket to epoll structure (errno " String::Number(errno) + ": " + Error::GetLastSystemError() + ')'); + return false; + } + + m_sockets.insert(socket); + + return true; + } + + void SocketPollerImpl::UnregisterSocket(SocketHandle socket) + { + NazaraAssert(IsRegistered(socket), "Socket is not registered"); + + m_activeSockets.erase(socket); + m_sockets.erase(socket); + + if (epoll_ctl(m_handle, EPOLL_CTL_DEL, socket, nullptr) != 0) + NazaraWarning("An error occured while removing socket from epoll structure (errno " String::Number(errno) + ": " + Error::GetLastSystemError() + ')'); + } + + int SocketPollerImpl::Wait(UInt64 msTimeout, SocketError* error) + { + int activeSockets; + + // Reset status of sockets + m_events.resize(m_sockets.size()); + std::memset(m_events.data(), 0, m_events.size() * sizeof(epoll_event)); + + activeSockets = epoll_wait(m_handle, m_events.data(), static_cast(m_events.size()), static_cast(msTimeout)); + + m_activeSockets.clear(); + if (activeSockets > 0U) + { + int socketCount = activeSockets; + for (int i = 0; i < socketCount; ++i) + { + if (m_events[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) + { + m_activeSockets.insert(m_events[i].data.fd); + if (m_events[i].events & EPOLLERR) + NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll with EPOLLERR status"); + } + else + { + NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll without EPOLLIN (events: 0x" + String::Number(m_events[i].events, 16) + ')'); + activeSockets--; + } + } + } + + return activeSockets; + } +} diff --git a/src/Nazara/Network/Linux/SocketPollerImpl.hpp b/src/Nazara/Network/Linux/SocketPollerImpl.hpp new file mode 100644 index 000000000..a6c5ad9f8 --- /dev/null +++ b/src/Nazara/Network/Linux/SocketPollerImpl.hpp @@ -0,0 +1,43 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SOCKETPOLLERIMPL_HPP +#define NAZARA_SOCKETPOLLERIMPL_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class SocketPollerImpl + { + public: + SocketPollerImpl(); + ~SocketPollerImpl(); + + void Clear(); + + bool IsReady(SocketHandle socket) const; + bool IsRegistered(SocketHandle socket) const; + + bool RegisterSocket(SocketHandle socket); + void UnregisterSocket(SocketHandle socket); + + int Wait(UInt64 msTimeout, SocketError* error); + + private: + std::unordered_set m_activeSockets; + std::unordered_set m_sockets; + std::vector m_events; + int m_handle; + }; +} + +#endif // NAZARA_SOCKETPOLLERIMPL_HPP