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