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