X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsocketengines%2Fsocketengine_poll.cpp;h=d25f21d9462d8b40bec004d2ba956c7f25b319bf;hb=aa692dc1039b63deef7886e914ec499abe7facaf;hp=d32d31cc4cc7b75c0412faa64460d31945aaedd0;hpb=d13dd19906631e88165ad51073ad1054b2d16dfc;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp index d32d31cc4..d25f21d94 100644 --- a/src/socketengines/socketengine_poll.cpp +++ b/src/socketengines/socketengine_poll.cpp @@ -1,201 +1,217 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2009 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits + * Copyright (C) 2014-2015 Attila Molnar + * Copyright (C) 2014, 2017 Adam + * Copyright (C) 2013, 2016-2017 Sadie Powell + * Copyright (C) 2012 Robby + * Copyright (C) 2009-2010 Daniel De Graaf + * Copyright (C) 2009 Uli Schlachter + * Copyright (C) 2008 Robin Burchell * - * This program is free but copyrighted software; see - * the file COPYING for details. + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. * - * --------------------------------------------------- + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + #include "inspircd.h" -#include "exitcodes.h" -#include "socketengines/socketengine_poll.h" -#include -PollEngine::PollEngine(InspIRCd* Instance) : SocketEngine(Instance) +#include +#include + +/** A specialisation of the SocketEngine class, designed to use poll(). + */ +namespace +{ + /** These are used by poll() to hold socket events + */ + std::vector events(16); + /** This vector maps fds to an index in the events array. + */ + std::vector fd_mappings(16, -1); +} + +void SocketEngine::Init() { - // Poll requires no special setup (which is nice). - CurrentSetSize = 0; - MAX_DESCRIPTORS = 0; + LookupMaxFds(); +} - ref = new EventHandler* [GetMaxFds()]; - events = new struct pollfd[GetMaxFds()]; +void SocketEngine::Deinit() +{ +} - memset(events, 0, GetMaxFds() * sizeof(struct pollfd)); - memset(ref, 0, GetMaxFds() * sizeof(EventHandler*)); +void SocketEngine::RecoverFromFork() +{ } -PollEngine::~PollEngine() +static int mask_to_poll(int event_mask) { - // No destruction required, either. - delete[] ref; - delete[] events; + int rv = 0; + if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ)) + rv |= POLLIN; + if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) + rv |= POLLOUT; + return rv; } -bool PollEngine::AddFd(EventHandler* eh) +bool SocketEngine::AddFd(EventHandler* eh, int event_mask) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds()); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd); return false; } - if (GetRemainingFds() <= 1) + if (static_cast(fd) < fd_mappings.size() && fd_mappings[fd] != -1) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"No remaining FDs cannot add fd: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd); return false; } - if (ref[fd]) + unsigned int index = CurrentSetSize; + + if (!SocketEngine::AddFdRef(eh)) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd); return false; } - ref[fd] = eh; - events[fd].fd = fd; - if (eh->Readable()) - { - events[fd].events = POLLIN; - } - else - { - events[fd].events = POLLOUT; - } + while (static_cast(fd) >= fd_mappings.size()) + fd_mappings.resize(fd_mappings.size() * 2, -1); + fd_mappings[fd] = index; - ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d)", fd, events[fd].events); - CurrentSetSize++; - return true; -} + ResizeDouble(events); + events[index].fd = fd; + events[index].events = mask_to_poll(event_mask); -void PollEngine::WantWrite(EventHandler* eh) -{ - events[eh->GetFd()].events = POLLIN | POLLOUT; + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index); + eh->SetEventMask(event_mask); + return true; } -bool PollEngine::DelFd(EventHandler* eh, bool force) +void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + if (fd < 0 || static_cast(fd) >= fd_mappings.size() || fd_mappings[fd] == -1) { - ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds()); - return false; + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd()); + return; } - events[fd].fd = -1; - events[fd].events = 0; - - CurrentSetSize--; - ref[fd] = NULL; - - ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d", fd); - return true; + events[fd_mappings[fd]].events = mask_to_poll(new_mask); } -int PollEngine::GetMaxFds() +void SocketEngine::DelFd(EventHandler* eh) { -#ifdef LINUX - if (MAX_DESCRIPTORS) - return MAX_DESCRIPTORS; - - int max = ulimit(4, 0); - if (max > 0) + int fd = eh->GetFd(); + if (fd < 0) { - MAX_DESCRIPTORS = max; - return max; + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd); + return; } - else + + if (static_cast(fd) >= fd_mappings.size() || fd_mappings[fd] == -1) { - MAX_DESCRIPTORS = 0; - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno)); - printf("ERROR: Can't determine maximum number of open sockets: %s\n", strerror(errno))); - ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd); + return; } - return 0; -#endif -#ifdef FREEBSD - if (!MAX_DESCRIPTORS) + + unsigned int index = fd_mappings[fd]; + unsigned int last_index = CurrentSetSize - 1; + int last_fd = events[last_index].fd; + + if (index != last_index) { - int mib[2], maxfiles; - size_t len; - - mib[0] = CTL_KERN; - mib[1] = KERN_MAXFILES; - len = sizeof(maxfiles); - sysctl(mib, 2, &maxfiles, &len, NULL, 0); - MAX_DESCRIPTORS = maxfiles; - return maxfiles; + // We need to move the last fd we got into this gap (gaps are evil!) + + // So update the mapping for the last fd to its new position + fd_mappings[last_fd] = index; + + // move last_fd from last_index into index + events[index].fd = last_fd; + events[index].events = events[last_index].events; } - return MAX_DESCRIPTORS; -#endif -} -int PollEngine::GetRemainingFds() -{ - return MAX_DESCRIPTORS - CurrentSetSize; + // Now remove all data for the last fd we got into out list. + // Above code made sure this always is right + fd_mappings[fd] = -1; + events[last_index].fd = 0; + events[last_index].events = 0; + + SocketEngine::DelFdRef(eh); + + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) " + "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index); } -int PollEngine::DispatchEvents() +int SocketEngine::DispatchEvents() { - int i = poll(events, GetMaxFds() - 1, 1000); - int fd = 0; - socklen_t codesize = sizeof(int); - int errcode; + int i = poll(&events[0], CurrentSetSize, 1000); int processed = 0; + ServerInstance->UpdateTime(); - if (i > 0) + for (size_t index = 0; index < CurrentSetSize && processed < i; index++) { - for (fd = 0; fd < GetMaxFds() - 1 && processed != i; fd++) - { - if (events[fd].revents) - processed++; + struct pollfd& pfd = events[index]; - if (events[fd].revents & POLLHUP) - { - if (ref[fd]) - ref[fd]->HandleEvent(EVENT_ERROR, 0); - continue; - } - - if (events[fd].revents & POLLERR) - { - // Get error number - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) - errcode = errno; - if (ref[fd]) - ref[fd]->HandleEvent(EVENT_ERROR, errcode); - continue; - } + // Copy these in case the vector gets resized and pfd invalidated + const int fd = pfd.fd; + const short revents = pfd.revents; + + if (revents) + processed++; + + EventHandler* eh = GetRef(fd); + if (!eh) + continue; - if (events[fd].revents & POLLOUT) - { - // Switch to wanting read again - // event handlers have to request to write again if they need it - events[fd].events = POLLIN; + if (revents & POLLHUP) + { + eh->OnEventHandlerError(0); + continue; + } + if (revents & POLLERR) + { + // Get error number + socklen_t codesize = sizeof(int); + int errcode; + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + errcode = errno; + eh->OnEventHandlerError(errcode); + continue; + } - if (ref[fd]) - ref[fd]->HandleEvent(EVENT_WRITE); - } + if (revents & POLLIN) + { + eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK); + eh->OnEventHandlerRead(); + if (eh != GetRef(fd)) + // whoops, deleted out from under us + continue; + } - if (events[fd].revents & POLLIN) - { - if (ref[fd]) - ref[fd]->HandleEvent(EVENT_READ); - } + if (revents & POLLOUT) + { + int mask = eh->GetEventMask(); + mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE); + eh->SetEventMask(mask); + + // The vector could've been resized, reference can be invalid by now; don't use it + events[index].events = mask_to_poll(mask); + eh->OnEventHandlerWrite(); } } return i; } - -std::string PollEngine::GetName() -{ - return "poll"; -} -