]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/socketengines/socketengine_epoll.cpp
Merge pull request #1162 from SaberUK/insp20+fix-deinstall
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
index 672ff4a7b9d3bb292e85cbbad652b5df5a8d1a23..f2837777ad7ec7a3ac11e6116be84bc70ca9ca0e 100644 (file)
@@ -1,23 +1,60 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
  *
- * 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 <http://www.gnu.org/licenses/>.
  */
 
+
+#include <vector>
+#include <string>
+#include <map>
 #include "inspircd.h"
 #include "exitcodes.h"
-#include "socketengines/socketengine_epoll.h"
+#include "socketengine.h"
+#include <sys/epoll.h>
 #include <ulimit.h>
+#include <iostream>
+#define EP_DELAY 5
+
+/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
+ */
+class EPollEngine : public SocketEngine
+{
+private:
+       /** These are used by epoll() to hold socket events
+        */
+       struct epoll_event* events;
+       int EngineHandle;
+public:
+       /** Create a new EPollEngine
+        */
+       EPollEngine();
+       /** Delete an EPollEngine
+        */
+       virtual ~EPollEngine();
+       virtual bool AddFd(EventHandler* eh, int event_mask);
+       virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
+       virtual void DelFd(EventHandler* eh);
+       virtual int DispatchEvents();
+       virtual std::string GetName();
+};
 
 EPollEngine::EPollEngine()
 {
+       CurrentSetSize = 0;
        int max = ulimit(4, 0);
        if (max > 0)
        {
@@ -26,8 +63,8 @@ EPollEngine::EPollEngine()
        else
        {
                ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
-               printf("ERROR: Can't determine maximum number of open sockets!\n");
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
 
        // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
@@ -37,9 +74,9 @@ EPollEngine::EPollEngine()
        {
                ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
                ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
-               printf("ERROR: Could not initialize epoll socket engine: %s\n", strerror(errno));
-               printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n");
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
+               std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
 
        ref = new EventHandler* [GetMaxFds()];
@@ -55,15 +92,15 @@ EPollEngine::~EPollEngine()
        delete[] events;
 }
 
-static int mask_to_epoll(int event_mask)
+static unsigned mask_to_epoll(int event_mask)
 {
-       int rv = 0;
-       if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE))
+       unsigned rv = 0;
+       if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
        {
                // we need to use standard polling on this FD
                if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
                        rv |= EPOLLIN;
-               if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE))
+               if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
                        rv |= EPOLLOUT;
        }
        else
@@ -114,8 +151,8 @@ bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
 
 void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
 {
-       int old_events = mask_to_epoll(old_mask);
-       int new_events = mask_to_epoll(new_mask);
+       unsigned old_events = mask_to_epoll(old_mask);
+       unsigned new_events = mask_to_epoll(new_mask);
        if (old_events != new_events)
        {
                // ok, we actually have something to tell the kernel about
@@ -127,13 +164,13 @@ void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
        }
 }
 
-bool EPollEngine::DelFd(EventHandler* eh, bool force)
+void EPollEngine::DelFd(EventHandler* eh)
 {
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > GetMaxFds() - 1))
        {
                ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
-               return false;
+               return;
        }
 
        struct epoll_event ev;
@@ -141,17 +178,15 @@ bool EPollEngine::DelFd(EventHandler* eh, bool force)
        ev.data.fd = fd;
        int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
 
-       if (i < 0 && !force)
+       if (i < 0)
        {
-               ServerInstance->Logs->Log("SOCKET",DEBUG,"Cant remove socket: %s", strerror(errno));
-               return false;
+               ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
        }
 
        ref[fd] = NULL;
 
        ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
        CurrentSetSize--;
-       return true;
 }
 
 int EPollEngine::DispatchEvents()
@@ -159,6 +194,7 @@ int EPollEngine::DispatchEvents()
        socklen_t codesize = sizeof(int);
        int errcode;
        int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
+       ServerInstance->UpdateTime();
 
        TotalEvents += i;
 
@@ -166,7 +202,11 @@ int EPollEngine::DispatchEvents()
        {
                EventHandler* eh = ref[events[j].data.fd];
                if (!eh)
+               {
+                       ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
+                       epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
                        continue;
+               }
                if (events[j].events & EPOLLHUP)
                {
                        ErrorEvents++;
@@ -182,16 +222,31 @@ int EPollEngine::DispatchEvents()
                        eh->HandleEvent(EVENT_ERROR, errcode);
                        continue;
                }
+               int mask = eh->GetEventMask();
+               if (events[j].events & EPOLLIN)
+                       mask &= ~FD_READ_WILL_BLOCK;
+               if (events[j].events & EPOLLOUT)
+               {
+                       mask &= ~FD_WRITE_WILL_BLOCK;
+                       if (mask & FD_WANT_SINGLE_WRITE)
+                       {
+                               int nm = mask & ~FD_WANT_SINGLE_WRITE;
+                               OnSetEvent(eh, mask, nm);
+                               mask = nm;
+                       }
+               }
+               SetEventMask(eh, mask);
                if (events[j].events & EPOLLIN)
                {
                        ReadEvents++;
-                       SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
                        eh->HandleEvent(EVENT_READ);
+                       if (eh != ref[events[j].data.fd])
+                               // whoa! we got deleted, better not give out the write event
+                               continue;
                }
                if (events[j].events & EPOLLOUT)
                {
                        WriteEvents++;
-                       SetEventMask(eh, eh->GetEventMask() & ~FD_WRITE_WILL_BLOCK);
                        eh->HandleEvent(EVENT_WRITE);
                }
        }
@@ -204,3 +259,7 @@ std::string EPollEngine::GetName()
        return "epoll";
 }
 
+SocketEngine* CreateSocketEngine()
+{
+       return new EPollEngine;
+}