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