]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
37f4b6836a09038d3b3abd3199c187a4608c9716
[user/henk/code/inspircd.git] / src / socketengines / socketengine_poll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
6  *   Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc>
7  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #ifndef SOCKETENGINE_POLL
24 #define SOCKETENGINE_POLL
25
26 #include <iostream>
27 #include <vector>
28 #include <string>
29 #include <map>
30 #include "exitcodes.h"
31 #include "inspircd.h"
32 #include "socketengine.h"
33
34 #ifndef _WIN32
35         #ifndef __USE_XOPEN
36             #define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
37         #endif
38         #include <poll.h>
39         #include <sys/poll.h>
40 #else
41         /* *grumble* */
42         #define struct pollfd WSAPOLLFD
43         #define poll WSAPoll
44 #endif
45
46 class InspIRCd;
47
48 /** A specialisation of the SocketEngine class, designed to use poll().
49  */
50 class PollEngine : public SocketEngine
51 {
52 private:
53         /** These are used by poll() to hold socket events
54          */
55         struct pollfd *events;
56         /** This map maps fds to an index in the events array.
57          */
58         std::map<int, unsigned int> fd_mappings;
59 public:
60         /** Create a new PollEngine
61          */
62         PollEngine();
63         /** Delete a PollEngine
64          */
65         virtual ~PollEngine();
66         virtual bool AddFd(EventHandler* eh, int event_mask);
67         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
68         virtual EventHandler* GetRef(int fd);
69         virtual void DelFd(EventHandler* eh);
70         virtual int DispatchEvents();
71         virtual std::string GetName();
72 };
73
74 #endif
75
76 #ifdef BSD
77         #include <sys/sysctl.h>
78 #else
79         #include <ulimit.h>
80 #endif
81
82 PollEngine::PollEngine()
83 {
84         CurrentSetSize = 0;
85 #ifdef BSD
86         int mib[2];
87         size_t len;
88
89         mib[0] = CTL_KERN;
90 #ifdef KERN_MAXFILESPERPROC
91         mib[1] = KERN_MAXFILESPERPROC;
92 #else
93         mib[1] = KERN_MAXFILES;
94 #endif
95         len = sizeof(MAX_DESCRIPTORS);
96         sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
97 #else
98         int max = ulimit(4, 0);
99         if (max > 0)
100         {
101                 MAX_DESCRIPTORS = max;
102         }
103         else
104         {
105                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
106                 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
107                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
108         }
109 #endif
110
111         ref = new EventHandler* [GetMaxFds()];
112         events = new struct pollfd[GetMaxFds()];
113
114         memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
115         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
116 }
117
118 PollEngine::~PollEngine()
119 {
120         // No destruction required, either.
121         delete[] ref;
122         delete[] events;
123 }
124
125 static int mask_to_poll(int event_mask)
126 {
127         int rv = 0;
128         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
129                 rv |= POLLIN;
130         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
131                 rv |= POLLOUT;
132         return rv;
133 }
134
135 bool PollEngine::AddFd(EventHandler* eh, int event_mask)
136 {
137         int fd = eh->GetFd();
138         if ((fd < 0) || (fd > GetMaxFds() - 1))
139         {
140                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
141                 return false;
142         }
143
144         if (fd_mappings.find(fd) != fd_mappings.end())
145         {
146                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
147                 return false;
148         }
149
150         unsigned int index = CurrentSetSize;
151
152         fd_mappings[fd] = index;
153         ref[index] = eh;
154         events[index].fd = fd;
155         events[index].events = mask_to_poll(event_mask);
156
157         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
158         SocketEngine::SetEventMask(eh, event_mask);
159         CurrentSetSize++;
160         return true;
161 }
162
163 EventHandler* PollEngine::GetRef(int fd)
164 {
165         std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
166         if (it == fd_mappings.end())
167                 return NULL;
168         return ref[it->second];
169 }
170
171 void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
172 {
173         std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
174         if (it == fd_mappings.end())
175         {
176                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
177                 return;
178         }
179
180         events[it->second].events = mask_to_poll(new_mask);
181 }
182
183 void PollEngine::DelFd(EventHandler* eh)
184 {
185         int fd = eh->GetFd();
186         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
187         {
188                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
189                 return;
190         }
191
192         std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
193         if (it == fd_mappings.end())
194         {
195                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
196                 return;
197         }
198
199         unsigned int index = it->second;
200         unsigned int last_index = CurrentSetSize - 1;
201         int last_fd = events[last_index].fd;
202
203         if (index != last_index)
204         {
205                 // We need to move the last fd we got into this gap (gaps are evil!)
206
207                 // So update the mapping for the last fd to its new position
208                 fd_mappings[last_fd] = index;
209
210                 // move last_fd from last_index into index
211                 events[index].fd = last_fd;
212                 events[index].events = events[last_index].events;
213
214                 ref[index] = ref[last_index];
215         }
216
217         // Now remove all data for the last fd we got into out list.
218         // Above code made sure this always is right
219         fd_mappings.erase(it);
220         events[last_index].fd = 0;
221         events[last_index].events = 0;
222         ref[last_index] = NULL;
223
224         CurrentSetSize--;
225
226         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
227                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
228 }
229
230 int PollEngine::DispatchEvents()
231 {
232         int i = poll(events, CurrentSetSize, 1000);
233         int index;
234         socklen_t codesize = sizeof(int);
235         int errcode;
236         int processed = 0;
237         ServerInstance->UpdateTime();
238
239         if (i > 0)
240         {
241                 for (index = 0; index < CurrentSetSize && processed != i; index++)
242                 {
243                         if (events[index].revents)
244                                 processed++;
245                         EventHandler* eh = ref[index];
246                         if (!eh)
247                                 continue;
248
249                         if (events[index].revents & POLLHUP)
250                         {
251                                 eh->HandleEvent(EVENT_ERROR, 0);
252                                 continue;
253                         }
254
255                         if (events[index].revents & POLLERR)
256                         {
257                                 // Get fd
258                                 int fd = events[index].fd;
259
260                                 // Get error number
261                                 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
262                                         errcode = errno;
263                                 eh->HandleEvent(EVENT_ERROR, errcode);
264                                 continue;
265                         }
266
267                         if (events[index].revents & POLLIN)
268                         {
269                                 SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
270                                 eh->HandleEvent(EVENT_READ);
271                                 if (eh != ref[index])
272                                         // whoops, deleted out from under us
273                                         continue;
274                         }
275
276                         if (events[index].revents & POLLOUT)
277                         {
278                                 int mask = eh->GetEventMask();
279                                 mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
280                                 SetEventMask(eh, mask);
281                                 events[index].events = mask_to_poll(mask);
282                                 eh->HandleEvent(EVENT_WRITE);
283                         }
284                 }
285         }
286
287         return i;
288 }
289
290 std::string PollEngine::GetName()
291 {
292         return "poll";
293 }
294
295 SocketEngine* CreateSocketEngine()
296 {
297         return new PollEngine;
298 }