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