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