]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
New socketengine stuff:
[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 #ifndef SOCKETENGINE_POLL
25 #define SOCKETENGINE_POLL
26
27 #include <iostream>
28 #include <vector>
29 #include <string>
30 #include <map>
31 #include "exitcodes.h"
32 #include "inspircd.h"
33 #include "socketengine.h"
34
35 #ifndef _WIN32
36 # ifndef __USE_XOPEN
37 #  define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
38 # endif
39 # include <poll.h>
40 # include <sys/poll.h>
41 # include <sys/resource.h>
42 #else
43 # define struct pollfd WSAPOLLFD
44 # define poll WSAPoll
45 #endif
46
47 class InspIRCd;
48
49 /** A specialisation of the SocketEngine class, designed to use poll().
50  */
51 class PollEngine : public SocketEngine
52 {
53 private:
54         /** These are used by poll() to hold socket events
55          */
56         std::vector<struct pollfd> events;
57         /** This vector maps fds to an index in the events array.
58          */
59         std::vector<int> fd_mappings;
60 public:
61         /** Create a new PollEngine
62          */
63         PollEngine();
64         virtual bool AddFd(EventHandler* eh, int event_mask);
65         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
66         virtual void DelFd(EventHandler* eh);
67         virtual int DispatchEvents();
68         virtual std::string GetName();
69 };
70
71 #endif
72
73 PollEngine::PollEngine() : events(1), fd_mappings(1)
74 {
75         CurrentSetSize = 0;
76         struct rlimit limits;
77         if (!getrlimit(RLIMIT_NOFILE, &limits))
78         {
79                 MAX_DESCRIPTORS = limits.rlim_cur;
80         }
81         else
82         {
83                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
84                 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
85                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
86         }
87 }
88
89 static int mask_to_poll(int event_mask)
90 {
91         int rv = 0;
92         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
93                 rv |= POLLIN;
94         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
95                 rv |= POLLOUT;
96         return rv;
97 }
98
99 bool PollEngine::AddFd(EventHandler* eh, int event_mask)
100 {
101         int fd = eh->GetFd();
102         if ((fd < 0) || (fd > GetMaxFds() - 1))
103         {
104                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
105                 return false;
106         }
107
108         if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
109         {
110                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
111                 return false;
112         }
113
114         if (!SocketEngine::AddFd(eh))
115         {
116                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
117                 return false;
118         }
119
120         unsigned int index = CurrentSetSize;
121
122         while (static_cast<unsigned int>(fd) >= fd_mappings.size())
123                 fd_mappings.resize(fd_mappings.size() * 2, -1);
124         fd_mappings[fd] = index;
125
126         ResizeDouble(events);
127         events[index].fd = fd;
128         events[index].events = mask_to_poll(event_mask);
129
130         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
131         SocketEngine::SetEventMask(eh, event_mask);
132         CurrentSetSize++;
133         return true;
134 }
135
136 void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
137 {
138         int fd = eh->GetFd();
139         if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
140         {
141                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
142                 return;
143         }
144
145         events[fd_mappings[fd]].events = mask_to_poll(new_mask);
146 }
147
148 void PollEngine::DelFd(EventHandler* eh)
149 {
150         int fd = eh->GetFd();
151         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
152         {
153                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
154                 return;
155         }
156
157         if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
158         {
159                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
160                 return;
161         }
162
163         unsigned int index = fd_mappings[fd];
164         unsigned int last_index = CurrentSetSize - 1;
165         int last_fd = events[last_index].fd;
166
167         if (index != last_index)
168         {
169                 // We need to move the last fd we got into this gap (gaps are evil!)
170
171                 // So update the mapping for the last fd to its new position
172                 fd_mappings[last_fd] = index;
173
174                 // move last_fd from last_index into index
175                 events[index].fd = last_fd;
176                 events[index].events = events[last_index].events;
177         }
178
179         // Now remove all data for the last fd we got into out list.
180         // Above code made sure this always is right
181         fd_mappings[fd] = -1;
182         events[last_index].fd = 0;
183         events[last_index].events = 0;
184
185         SocketEngine::DelFd(eh);
186
187         CurrentSetSize--;
188
189         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
190                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
191 }
192
193 int PollEngine::DispatchEvents()
194 {
195         int i = poll(&events[0], CurrentSetSize, 1000);
196         int index;
197         socklen_t codesize = sizeof(int);
198         int errcode;
199         int processed = 0;
200         ServerInstance->UpdateTime();
201
202         for (index = 0; index < CurrentSetSize && processed < i; index++)
203         {
204                 struct pollfd& pfd = events[index];
205
206                 if (pfd.revents)
207                         processed++;
208
209                 EventHandler* eh = GetRef(pfd.fd);
210                 if (!eh)
211                         continue;
212
213                 if (pfd.revents & POLLHUP)
214                 {
215                         eh->HandleEvent(EVENT_ERROR, 0);
216                         continue;
217                 }
218
219                 if (pfd.revents & POLLERR)
220                 {
221                         // Get error number
222                         if (getsockopt(pfd.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
223                                 errcode = errno;
224                         eh->HandleEvent(EVENT_ERROR, errcode);
225                         continue;
226                 }
227
228                 if (pfd.revents & POLLIN)
229                 {
230                         SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
231                         eh->HandleEvent(EVENT_READ);
232                         if (eh != GetRef(pfd.fd))
233                                 // whoops, deleted out from under us
234                                 continue;
235                 }
236
237                 if (pfd.revents & POLLOUT)
238                 {
239                         int mask = eh->GetEventMask();
240                         mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
241                         SetEventMask(eh, mask);
242                         pfd.events = mask_to_poll(mask);
243                         eh->HandleEvent(EVENT_WRITE);
244                 }
245         }
246
247         return i;
248 }
249
250 std::string PollEngine::GetName()
251 {
252         return "poll";
253 }
254
255 SocketEngine* CreateSocketEngine()
256 {
257         return new PollEngine;
258 }