]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
Switch <stdint.h> test to use a test file too.
[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         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()
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", 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",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
76                 ServerInstance->Logs->Log("SOCKET",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         ref = new EventHandler* [GetMaxFds()];
83         events = new struct epoll_event[GetMaxFds()];
84
85         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
86 }
87
88 EPollEngine::~EPollEngine()
89 {
90         this->Close(EngineHandle);
91         delete[] ref;
92         delete[] events;
93 }
94
95 static unsigned mask_to_epoll(int event_mask)
96 {
97         unsigned rv = 0;
98         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
99         {
100                 // we need to use standard polling on this FD
101                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
102                         rv |= EPOLLIN;
103                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
104                         rv |= EPOLLOUT;
105         }
106         else
107         {
108                 // we can use edge-triggered polling on this FD
109                 rv = EPOLLET;
110                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
111                         rv |= EPOLLIN;
112                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
113                         rv |= EPOLLOUT;
114         }
115         return rv;
116 }
117
118 bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
119 {
120         int fd = eh->GetFd();
121         if ((fd < 0) || (fd > GetMaxFds() - 1))
122         {
123                 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
124                 return false;
125         }
126
127         if (ref[fd])
128         {
129                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
130                 return false;
131         }
132
133         struct epoll_event ev;
134         memset(&ev,0,sizeof(ev));
135         ev.events = mask_to_epoll(event_mask);
136         ev.data.fd = fd;
137         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
138         if (i < 0)
139         {
140                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
141                 return false;
142         }
143
144         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
145
146         ref[fd] = eh;
147         SocketEngine::SetEventMask(eh, event_mask);
148         CurrentSetSize++;
149         return true;
150 }
151
152 void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
153 {
154         unsigned old_events = mask_to_epoll(old_mask);
155         unsigned new_events = mask_to_epoll(new_mask);
156         if (old_events != new_events)
157         {
158                 // ok, we actually have something to tell the kernel about
159                 struct epoll_event ev;
160                 memset(&ev,0,sizeof(ev));
161                 ev.events = new_events;
162                 ev.data.fd = eh->GetFd();
163                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
164         }
165 }
166
167 void EPollEngine::DelFd(EventHandler* eh)
168 {
169         int fd = eh->GetFd();
170         if ((fd < 0) || (fd > GetMaxFds() - 1))
171         {
172                 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
173                 return;
174         }
175
176         struct epoll_event ev;
177         memset(&ev,0,sizeof(ev));
178         ev.data.fd = fd;
179         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
180
181         if (i < 0)
182         {
183                 ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
184         }
185
186         ref[fd] = NULL;
187
188         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
189         CurrentSetSize--;
190 }
191
192 int EPollEngine::DispatchEvents()
193 {
194         socklen_t codesize = sizeof(int);
195         int errcode;
196         int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
197         ServerInstance->UpdateTime();
198
199         TotalEvents += i;
200
201         for (int j = 0; j < i; j++)
202         {
203                 EventHandler* eh = ref[events[j].data.fd];
204                 if (!eh)
205                 {
206                         ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
207                         epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
208                         continue;
209                 }
210                 if (events[j].events & EPOLLHUP)
211                 {
212                         ErrorEvents++;
213                         eh->HandleEvent(EVENT_ERROR, 0);
214                         continue;
215                 }
216                 if (events[j].events & EPOLLERR)
217                 {
218                         ErrorEvents++;
219                         /* Get error number */
220                         if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
221                                 errcode = errno;
222                         eh->HandleEvent(EVENT_ERROR, errcode);
223                         continue;
224                 }
225                 int mask = eh->GetEventMask();
226                 if (events[j].events & EPOLLIN)
227                         mask &= ~FD_READ_WILL_BLOCK;
228                 if (events[j].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 (events[j].events & EPOLLIN)
240                 {
241                         ReadEvents++;
242                         eh->HandleEvent(EVENT_READ);
243                         if (eh != ref[events[j].data.fd])
244                                 // whoa! we got deleted, better not give out the write event
245                                 continue;
246                 }
247                 if (events[j].events & EPOLLOUT)
248                 {
249                         WriteEvents++;
250                         eh->HandleEvent(EVENT_WRITE);
251                 }
252         }
253
254         return i;
255 }
256
257 std::string EPollEngine::GetName()
258 {
259         return "epoll";
260 }
261
262 SocketEngine* CreateSocketEngine()
263 {
264         return new EPollEngine;
265 }