]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
79d69698a1d10ee95e40d966a44051d4e08a37b4
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include <vector>
22 #include <string>
23 #include <map>
24 #include "inspircd.h"
25 #include "exitcodes.h"
26 #include "socketengine.h"
27 #include <sys/epoll.h>
28 #include <ulimit.h>
29 #define EP_DELAY 5
30
31 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
32  */
33 class EPollEngine : public SocketEngine
34 {
35 private:
36         /** These are used by epoll() to hold socket events
37          */
38         struct epoll_event* events;
39         int EngineHandle;
40 public:
41         /** Create a new EPollEngine
42          */
43         EPollEngine();
44         /** Delete an EPollEngine
45          */
46         virtual ~EPollEngine();
47         virtual bool AddFd(EventHandler* eh, int event_mask);
48         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
49         virtual void DelFd(EventHandler* eh);
50         virtual int DispatchEvents();
51         virtual std::string GetName();
52 };
53
54 EPollEngine::EPollEngine()
55 {
56         int max = ulimit(4, 0);
57         if (max > 0)
58         {
59                 MAX_DESCRIPTORS = max;
60         }
61         else
62         {
63                 ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
64                 printf("ERROR: Can't determine maximum number of open sockets!\n");
65                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
66         }
67
68         // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
69         EngineHandle = epoll_create(GetMaxFds() / 4);
70
71         if (EngineHandle == -1)
72         {
73                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
74                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
75                 printf("ERROR: Could not initialize epoll socket engine: %s\n", strerror(errno));
76                 printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n");
77                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
78         }
79
80         ref = new EventHandler* [GetMaxFds()];
81         events = new struct epoll_event[GetMaxFds()];
82
83         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
84 }
85
86 EPollEngine::~EPollEngine()
87 {
88         this->Close(EngineHandle);
89         delete[] ref;
90         delete[] events;
91 }
92
93 static unsigned mask_to_epoll(int event_mask)
94 {
95         unsigned rv = 0;
96         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
97         {
98                 // we need to use standard polling on this FD
99                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
100                         rv |= EPOLLIN;
101                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
102                         rv |= EPOLLOUT;
103         }
104         else
105         {
106                 // we can use edge-triggered polling on this FD
107                 rv = EPOLLET;
108                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
109                         rv |= EPOLLIN;
110                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
111                         rv |= EPOLLOUT;
112         }
113         return rv;
114 }
115
116 bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
117 {
118         int fd = eh->GetFd();
119         if ((fd < 0) || (fd > GetMaxFds() - 1))
120         {
121                 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
122                 return false;
123         }
124
125         if (ref[fd])
126         {
127                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
128                 return false;
129         }
130
131         struct epoll_event ev;
132         memset(&ev,0,sizeof(ev));
133         ev.events = mask_to_epoll(event_mask);
134         ev.data.fd = fd;
135         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
136         if (i < 0)
137         {
138                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
139                 return false;
140         }
141
142         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
143
144         ref[fd] = eh;
145         SocketEngine::SetEventMask(eh, event_mask);
146         CurrentSetSize++;
147         return true;
148 }
149
150 void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
151 {
152         unsigned old_events = mask_to_epoll(old_mask);
153         unsigned new_events = mask_to_epoll(new_mask);
154         if (old_events != new_events)
155         {
156                 // ok, we actually have something to tell the kernel about
157                 struct epoll_event ev;
158                 memset(&ev,0,sizeof(ev));
159                 ev.events = new_events;
160                 ev.data.fd = eh->GetFd();
161                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
162         }
163 }
164
165 void EPollEngine::DelFd(EventHandler* eh)
166 {
167         int fd = eh->GetFd();
168         if ((fd < 0) || (fd > GetMaxFds() - 1))
169         {
170                 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
171                 return;
172         }
173
174         struct epoll_event ev;
175         memset(&ev,0,sizeof(ev));
176         ev.data.fd = fd;
177         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
178
179         if (i < 0)
180         {
181                 ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
182         }
183
184         ref[fd] = NULL;
185
186         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
187         CurrentSetSize--;
188 }
189
190 int EPollEngine::DispatchEvents()
191 {
192         socklen_t codesize = sizeof(int);
193         int errcode;
194         int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
195         ServerInstance->UpdateTime();
196
197         TotalEvents += i;
198
199         for (int j = 0; j < i; j++)
200         {
201                 EventHandler* eh = ref[events[j].data.fd];
202                 if (!eh)
203                 {
204                         ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
205                         epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
206                         continue;
207                 }
208                 if (events[j].events & EPOLLHUP)
209                 {
210                         ErrorEvents++;
211                         eh->HandleEvent(EVENT_ERROR, 0);
212                         continue;
213                 }
214                 if (events[j].events & EPOLLERR)
215                 {
216                         ErrorEvents++;
217                         /* Get error number */
218                         if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
219                                 errcode = errno;
220                         eh->HandleEvent(EVENT_ERROR, errcode);
221                         continue;
222                 }
223                 int mask = eh->GetEventMask();
224                 if (events[j].events & EPOLLIN)
225                         mask &= ~FD_READ_WILL_BLOCK;
226                 if (events[j].events & EPOLLOUT)
227                 {
228                         mask &= ~FD_WRITE_WILL_BLOCK;
229                         if (mask & FD_WANT_SINGLE_WRITE)
230                         {
231                                 int nm = mask & ~FD_WANT_SINGLE_WRITE;
232                                 OnSetEvent(eh, mask, nm);
233                                 mask = nm;
234                         }
235                 }
236                 SetEventMask(eh, mask);
237                 if (events[j].events & EPOLLIN)
238                 {
239                         ReadEvents++;
240                         eh->HandleEvent(EVENT_READ);
241                         if (eh != ref[events[j].data.fd])
242                                 // whoa! we got deleted, better not give out the write event
243                                 continue;
244                 }
245                 if (events[j].events & EPOLLOUT)
246                 {
247                         WriteEvents++;
248                         eh->HandleEvent(EVENT_WRITE);
249                 }
250         }
251
252         return i;
253 }
254
255 std::string EPollEngine::GetName()
256 {
257         return "epoll";
258 }
259
260 SocketEngine* CreateSocketEngine()
261 {
262         return new EPollEngine;
263 }