]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
New socketengine stuff:
[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         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         struct epoll_event ev;
170         memset(&ev,0,sizeof(ev));
171         ev.data.fd = fd;
172         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
173
174         if (i < 0)
175         {
176                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
177         }
178
179         SocketEngine::DelFd(eh);
180
181         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
182         CurrentSetSize--;
183 }
184
185 int EPollEngine::DispatchEvents()
186 {
187         socklen_t codesize = sizeof(int);
188         int errcode;
189         int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
190         ServerInstance->UpdateTime();
191
192         TotalEvents += i;
193
194         for (int j = 0; j < i; j++)
195         {
196                 struct epoll_event& ev = events[j];
197
198                 EventHandler* eh = GetRef(ev.data.fd);
199                 if (!eh)
200                 {
201                         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Got event on unknown fd: %d", events[j].data.fd);
202                         epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
203                         continue;
204                 }
205
206                 if (ev.events & EPOLLHUP)
207                 {
208                         ErrorEvents++;
209                         eh->HandleEvent(EVENT_ERROR, 0);
210                         continue;
211                 }
212
213                 if (ev.events & EPOLLERR)
214                 {
215                         ErrorEvents++;
216                         /* Get error number */
217                         if (getsockopt(ev.data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
218                                 errcode = errno;
219                         eh->HandleEvent(EVENT_ERROR, errcode);
220                         continue;
221                 }
222
223                 int mask = eh->GetEventMask();
224                 if (ev.events & EPOLLIN)
225                         mask &= ~FD_READ_WILL_BLOCK;
226                 if (ev.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 (ev.events & EPOLLIN)
238                 {
239                         ReadEvents++;
240                         eh->HandleEvent(EVENT_READ);
241                         if (eh != GetRef(ev.data.fd))
242                                 // whoa! we got deleted, better not give out the write event
243                                 continue;
244                 }
245                 if (ev.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 }