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