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