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