]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
Merge insp20
[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 # include <sys/resource.h>
41 #else
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 PollEngine::PollEngine()
77 {
78         CurrentSetSize = 0;
79         struct rlimit limits;
80         if (!getrlimit(RLIMIT_NOFILE, &limits))
81         {
82                 MAX_DESCRIPTORS = limits.rlim_cur;
83         }
84         else
85         {
86                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
87                 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
88                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
89         }
90
91         ref = new EventHandler* [GetMaxFds()];
92         events = new struct pollfd[GetMaxFds()];
93
94         memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
95         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
96 }
97
98 PollEngine::~PollEngine()
99 {
100         // No destruction required, either.
101         delete[] ref;
102         delete[] events;
103 }
104
105 static int mask_to_poll(int event_mask)
106 {
107         int rv = 0;
108         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
109                 rv |= POLLIN;
110         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
111                 rv |= POLLOUT;
112         return rv;
113 }
114
115 bool PollEngine::AddFd(EventHandler* eh, int event_mask)
116 {
117         int fd = eh->GetFd();
118         if ((fd < 0) || (fd > GetMaxFds() - 1))
119         {
120                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
121                 return false;
122         }
123
124         if (fd_mappings.find(fd) != fd_mappings.end())
125         {
126                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
127                 return false;
128         }
129
130         unsigned int index = CurrentSetSize;
131
132         fd_mappings[fd] = index;
133         ref[index] = eh;
134         events[index].fd = fd;
135         events[index].events = mask_to_poll(event_mask);
136
137         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
138         SocketEngine::SetEventMask(eh, event_mask);
139         CurrentSetSize++;
140         return true;
141 }
142
143 EventHandler* PollEngine::GetRef(int fd)
144 {
145         std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
146         if (it == fd_mappings.end())
147                 return NULL;
148         return ref[it->second];
149 }
150
151 void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
152 {
153         std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
154         if (it == fd_mappings.end())
155         {
156                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
157                 return;
158         }
159
160         events[it->second].events = mask_to_poll(new_mask);
161 }
162
163 void PollEngine::DelFd(EventHandler* eh)
164 {
165         int fd = eh->GetFd();
166         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
167         {
168                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
169                 return;
170         }
171
172         std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
173         if (it == fd_mappings.end())
174         {
175                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
176                 return;
177         }
178
179         unsigned int index = it->second;
180         unsigned int last_index = CurrentSetSize - 1;
181         int last_fd = events[last_index].fd;
182
183         if (index != last_index)
184         {
185                 // We need to move the last fd we got into this gap (gaps are evil!)
186
187                 // So update the mapping for the last fd to its new position
188                 fd_mappings[last_fd] = index;
189
190                 // move last_fd from last_index into index
191                 events[index].fd = last_fd;
192                 events[index].events = events[last_index].events;
193
194                 ref[index] = ref[last_index];
195         }
196
197         // Now remove all data for the last fd we got into out list.
198         // Above code made sure this always is right
199         fd_mappings.erase(it);
200         events[last_index].fd = 0;
201         events[last_index].events = 0;
202         ref[last_index] = NULL;
203
204         CurrentSetSize--;
205
206         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
207                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
208 }
209
210 int PollEngine::DispatchEvents()
211 {
212         int i = poll(events, CurrentSetSize, 1000);
213         int index;
214         socklen_t codesize = sizeof(int);
215         int errcode;
216         int processed = 0;
217         ServerInstance->UpdateTime();
218
219         if (i > 0)
220         {
221                 for (index = 0; index < CurrentSetSize && processed != i; index++)
222                 {
223                         if (events[index].revents)
224                                 processed++;
225                         EventHandler* eh = ref[index];
226                         if (!eh)
227                                 continue;
228
229                         if (events[index].revents & POLLHUP)
230                         {
231                                 eh->HandleEvent(EVENT_ERROR, 0);
232                                 continue;
233                         }
234
235                         if (events[index].revents & POLLERR)
236                         {
237                                 // Get fd
238                                 int fd = events[index].fd;
239
240                                 // Get error number
241                                 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
242                                         errcode = errno;
243                                 eh->HandleEvent(EVENT_ERROR, errcode);
244                                 continue;
245                         }
246
247                         if (events[index].revents & POLLIN)
248                         {
249                                 SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
250                                 eh->HandleEvent(EVENT_READ);
251                                 if (eh != ref[index])
252                                         // whoops, deleted out from under us
253                                         continue;
254                         }
255
256                         if (events[index].revents & POLLOUT)
257                         {
258                                 int mask = eh->GetEventMask();
259                                 mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
260                                 SetEventMask(eh, mask);
261                                 events[index].events = mask_to_poll(mask);
262                                 eh->HandleEvent(EVENT_WRITE);
263                         }
264                 }
265         }
266
267         return i;
268 }
269
270 std::string PollEngine::GetName()
271 {
272         return "poll";
273 }
274
275 SocketEngine* CreateSocketEngine()
276 {
277         return new PollEngine;
278 }