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