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