]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
09fa918c6887adf09e2e6b50e1b623a4bc96f420
[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 #include <iostream>
30 #define EP_DELAY 5
31
32 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
33  */
34 class EPollEngine : public SocketEngine
35 {
36 private:
37         /** These are used by epoll() to hold socket events
38          */
39         std::vector<struct epoll_event> events;
40         int EngineHandle;
41 public:
42         /** Create a new EPollEngine
43          */
44         EPollEngine();
45         /** Delete an EPollEngine
46          */
47         virtual ~EPollEngine();
48         virtual bool AddFd(EventHandler* eh, int event_mask);
49         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
50         virtual void DelFd(EventHandler* eh);
51         virtual int DispatchEvents();
52         virtual std::string GetName();
53 };
54
55 EPollEngine::EPollEngine() : events(1)
56 {
57         CurrentSetSize = 0;
58         int max = ulimit(4, 0);
59         if (max > 0)
60         {
61                 MAX_DESCRIPTORS = max;
62         }
63         else
64         {
65                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
66                 std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
67                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
68         }
69
70         // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
71         EngineHandle = epoll_create(GetMaxFds() / 4);
72
73         if (EngineHandle == -1)
74         {
75                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
76                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
77                 std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
78                 std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
79                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
80         }
81 }
82
83 EPollEngine::~EPollEngine()
84 {
85         this->Close(EngineHandle);
86 }
87
88 static unsigned mask_to_epoll(int event_mask)
89 {
90         unsigned rv = 0;
91         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
92         {
93                 // we need to use standard polling on this FD
94                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
95                         rv |= EPOLLIN;
96                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
97                         rv |= EPOLLOUT;
98         }
99         else
100         {
101                 // we can use edge-triggered polling on this FD
102                 rv = EPOLLET;
103                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
104                         rv |= EPOLLIN;
105                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
106                         rv |= EPOLLOUT;
107         }
108         return rv;
109 }
110
111 bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
112 {
113         int fd = eh->GetFd();
114         if ((fd < 0) || (fd > GetMaxFds() - 1))
115         {
116                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
117                 return false;
118         }
119
120         if (!SocketEngine::AddFd(eh))
121         {
122                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
123                 return false;
124         }
125
126         struct epoll_event ev;
127         memset(&ev, 0, sizeof(ev));
128         ev.events = mask_to_epoll(event_mask);
129         ev.data.fd = fd;
130         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
131         if (i < 0)
132         {
133                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
134                 return false;
135         }
136
137         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
138
139         SocketEngine::SetEventMask(eh, event_mask);
140         CurrentSetSize++;
141         ResizeDouble(events);
142
143         return true;
144 }
145
146 void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
147 {
148         unsigned old_events = mask_to_epoll(old_mask);
149         unsigned new_events = mask_to_epoll(new_mask);
150         if (old_events != new_events)
151         {
152                 // ok, we actually have something to tell the kernel about
153                 struct epoll_event ev;
154                 memset(&ev, 0, sizeof(ev));
155                 ev.events = new_events;
156                 ev.data.fd = eh->GetFd();
157                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
158         }
159 }
160
161 void EPollEngine::DelFd(EventHandler* eh)
162 {
163         int fd = eh->GetFd();
164         if ((fd < 0) || (fd > GetMaxFds() - 1))
165         {
166                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
167                 return;
168         }
169
170         // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL.
171         // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event,
172         // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.
173         struct epoll_event ev;
174         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
175
176         if (i < 0)
177         {
178                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
179         }
180
181         SocketEngine::DelFd(eh);
182
183         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
184         CurrentSetSize--;
185 }
186
187 int EPollEngine::DispatchEvents()
188 {
189         socklen_t codesize = sizeof(int);
190         int errcode;
191         int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
192         ServerInstance->UpdateTime();
193
194         TotalEvents += i;
195
196         for (int j = 0; j < i; j++)
197         {
198                 // Copy these in case the vector gets resized and ev invalidated
199                 const epoll_event ev = events[j];
200
201                 EventHandler* eh = GetRef(ev.data.fd);
202                 if (!eh)
203                 {
204                         ServerInstance->Logs->Log("SOCKET", LOG_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
209                 if (ev.events & EPOLLHUP)
210                 {
211                         ErrorEvents++;
212                         eh->HandleEvent(EVENT_ERROR, 0);
213                         continue;
214                 }
215
216                 if (ev.events & EPOLLERR)
217                 {
218                         ErrorEvents++;
219                         /* Get error number */
220                         if (getsockopt(ev.data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
221                                 errcode = errno;
222                         eh->HandleEvent(EVENT_ERROR, errcode);
223                         continue;
224                 }
225
226                 int mask = eh->GetEventMask();
227                 if (ev.events & EPOLLIN)
228                         mask &= ~FD_READ_WILL_BLOCK;
229                 if (ev.events & EPOLLOUT)
230                 {
231                         mask &= ~FD_WRITE_WILL_BLOCK;
232                         if (mask & FD_WANT_SINGLE_WRITE)
233                         {
234                                 int nm = mask & ~FD_WANT_SINGLE_WRITE;
235                                 OnSetEvent(eh, mask, nm);
236                                 mask = nm;
237                         }
238                 }
239                 SetEventMask(eh, mask);
240                 if (ev.events & EPOLLIN)
241                 {
242                         ReadEvents++;
243                         eh->HandleEvent(EVENT_READ);
244                         if (eh != GetRef(ev.data.fd))
245                                 // whoa! we got deleted, better not give out the write event
246                                 continue;
247                 }
248                 if (ev.events & EPOLLOUT)
249                 {
250                         WriteEvents++;
251                         eh->HandleEvent(EVENT_WRITE);
252                 }
253         }
254
255         return i;
256 }
257
258 std::string EPollEngine::GetName()
259 {
260         return "epoll";
261 }
262
263 SocketEngine* CreateSocketEngine()
264 {
265         return new EPollEngine;
266 }