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