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