]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
46a517c5105d8c1099cda8a5d4e8370225ffb6de
[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 };
64
65 PollEngine::PollEngine() : events(1), fd_mappings(1)
66 {
67         struct rlimit limits;
68         if (!getrlimit(RLIMIT_NOFILE, &limits))
69         {
70                 MAX_DESCRIPTORS = limits.rlim_cur;
71         }
72         else
73         {
74                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
75                 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
76                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
77         }
78 }
79
80 static int mask_to_poll(int event_mask)
81 {
82         int rv = 0;
83         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
84                 rv |= POLLIN;
85         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
86                 rv |= POLLOUT;
87         return rv;
88 }
89
90 bool PollEngine::AddFd(EventHandler* eh, int event_mask)
91 {
92         int fd = eh->GetFd();
93         if ((fd < 0) || (fd > GetMaxFds() - 1))
94         {
95                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
96                 return false;
97         }
98
99         if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
100         {
101                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
102                 return false;
103         }
104
105         unsigned int index = CurrentSetSize;
106
107         if (!SocketEngine::AddFdRef(eh))
108         {
109                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
110                 return false;
111         }
112
113         while (static_cast<unsigned int>(fd) >= fd_mappings.size())
114                 fd_mappings.resize(fd_mappings.size() * 2, -1);
115         fd_mappings[fd] = index;
116
117         ResizeDouble(events);
118         events[index].fd = fd;
119         events[index].events = mask_to_poll(event_mask);
120
121         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
122         SocketEngine::SetEventMask(eh, event_mask);
123         return true;
124 }
125
126 void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
127 {
128         int fd = eh->GetFd();
129         if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
130         {
131                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
132                 return;
133         }
134
135         events[fd_mappings[fd]].events = mask_to_poll(new_mask);
136 }
137
138 void PollEngine::DelFd(EventHandler* eh)
139 {
140         int fd = eh->GetFd();
141         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
142         {
143                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
144                 return;
145         }
146
147         if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
148         {
149                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
150                 return;
151         }
152
153         unsigned int index = fd_mappings[fd];
154         unsigned int last_index = CurrentSetSize - 1;
155         int last_fd = events[last_index].fd;
156
157         if (index != last_index)
158         {
159                 // We need to move the last fd we got into this gap (gaps are evil!)
160
161                 // So update the mapping for the last fd to its new position
162                 fd_mappings[last_fd] = index;
163
164                 // move last_fd from last_index into index
165                 events[index].fd = last_fd;
166                 events[index].events = events[last_index].events;
167         }
168
169         // Now remove all data for the last fd we got into out list.
170         // Above code made sure this always is right
171         fd_mappings[fd] = -1;
172         events[last_index].fd = 0;
173         events[last_index].events = 0;
174
175         SocketEngine::DelFdRef(eh);
176
177         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
178                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
179 }
180
181 int PollEngine::DispatchEvents()
182 {
183         int i = poll(&events[0], CurrentSetSize, 1000);
184         int processed = 0;
185         ServerInstance->UpdateTime();
186
187         for (int index = 0; index < CurrentSetSize && processed < i; index++)
188         {
189                 struct pollfd& pfd = events[index];
190
191                 // Copy these in case the vector gets resized and pfd invalidated
192                 const int fd = pfd.fd;
193                 const short revents = pfd.revents;
194
195                 if (revents)
196                         processed++;
197
198                 EventHandler* eh = GetRef(fd);
199                 if (!eh)
200                         continue;
201
202                 if (revents & POLLHUP)
203                 {
204                         eh->HandleEvent(EVENT_ERROR, 0);
205                         continue;
206                 }
207
208                 if (revents & POLLERR)
209                 {
210                         // Get error number
211                         socklen_t codesize = sizeof(int);
212                         int errcode;
213                         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
214                                 errcode = errno;
215                         eh->HandleEvent(EVENT_ERROR, errcode);
216                         continue;
217                 }
218
219                 if (revents & POLLIN)
220                 {
221                         SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
222                         eh->HandleEvent(EVENT_READ);
223                         if (eh != GetRef(fd))
224                                 // whoops, deleted out from under us
225                                 continue;
226                 }
227
228                 if (revents & POLLOUT)
229                 {
230                         int mask = eh->GetEventMask();
231                         mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
232                         SetEventMask(eh, mask);
233
234                         // The vector could've been resized, reference can be invalid by now; don't use it
235                         events[index].events = mask_to_poll(mask);
236                         eh->HandleEvent(EVENT_WRITE);
237                 }
238         }
239
240         return i;
241 }
242
243 SocketEngine* CreateSocketEngine()
244 {
245         return new PollEngine;
246 }