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