2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5 * Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
6 * Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc>
7 * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
9 * This file is part of InspIRCd. InspIRCd is free software: you can
10 * redistribute it and/or modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation, version 2.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #ifndef SOCKETENGINE_POLL
24 #define SOCKETENGINE_POLL
30 #include "exitcodes.h"
32 #include "socketengine.h"
36 #define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
42 #define struct pollfd WSAPOLLFD
48 /** A specialisation of the SocketEngine class, designed to use poll().
50 class PollEngine : public SocketEngine
53 /** These are used by poll() to hold socket events
55 struct pollfd *events;
56 /** This map maps fds to an index in the events array.
58 std::map<int, unsigned int> fd_mappings;
60 /** Create a new PollEngine
63 /** Delete a PollEngine
65 virtual ~PollEngine();
66 virtual bool AddFd(EventHandler* eh, int event_mask);
67 virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
68 virtual EventHandler* GetRef(int fd);
69 virtual void DelFd(EventHandler* eh);
70 virtual int DispatchEvents();
71 virtual std::string GetName();
77 #include <sys/sysctl.h>
82 PollEngine::PollEngine()
90 #ifdef KERN_MAXFILESPERPROC
91 mib[1] = KERN_MAXFILESPERPROC;
93 mib[1] = KERN_MAXFILES;
95 len = sizeof(MAX_DESCRIPTORS);
96 sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
98 int max = ulimit(4, 0);
101 MAX_DESCRIPTORS = max;
105 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
106 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
107 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
111 ref = new EventHandler* [GetMaxFds()];
112 events = new struct pollfd[GetMaxFds()];
114 memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
115 memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
118 PollEngine::~PollEngine()
120 // No destruction required, either.
125 static int mask_to_poll(int event_mask)
128 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
130 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
135 bool PollEngine::AddFd(EventHandler* eh, int event_mask)
137 int fd = eh->GetFd();
138 if ((fd < 0) || (fd > GetMaxFds() - 1))
140 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
144 if (fd_mappings.find(fd) != fd_mappings.end())
146 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
150 unsigned int index = CurrentSetSize;
152 fd_mappings[fd] = index;
154 events[index].fd = fd;
155 events[index].events = mask_to_poll(event_mask);
157 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
158 SocketEngine::SetEventMask(eh, event_mask);
163 EventHandler* PollEngine::GetRef(int fd)
165 std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
166 if (it == fd_mappings.end())
168 return ref[it->second];
171 void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
173 std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
174 if (it == fd_mappings.end())
176 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
180 events[it->second].events = mask_to_poll(new_mask);
183 void PollEngine::DelFd(EventHandler* eh)
185 int fd = eh->GetFd();
186 if ((fd < 0) || (fd > MAX_DESCRIPTORS))
188 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
192 std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
193 if (it == fd_mappings.end())
195 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
199 unsigned int index = it->second;
200 unsigned int last_index = CurrentSetSize - 1;
201 int last_fd = events[last_index].fd;
203 if (index != last_index)
205 // We need to move the last fd we got into this gap (gaps are evil!)
207 // So update the mapping for the last fd to its new position
208 fd_mappings[last_fd] = index;
210 // move last_fd from last_index into index
211 events[index].fd = last_fd;
212 events[index].events = events[last_index].events;
214 ref[index] = ref[last_index];
217 // Now remove all data for the last fd we got into out list.
218 // Above code made sure this always is right
219 fd_mappings.erase(it);
220 events[last_index].fd = 0;
221 events[last_index].events = 0;
222 ref[last_index] = NULL;
226 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
227 "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
230 int PollEngine::DispatchEvents()
232 int i = poll(events, CurrentSetSize, 1000);
234 socklen_t codesize = sizeof(int);
237 ServerInstance->UpdateTime();
241 for (index = 0; index < CurrentSetSize && processed != i; index++)
243 if (events[index].revents)
245 EventHandler* eh = ref[index];
249 if (events[index].revents & POLLHUP)
251 eh->HandleEvent(EVENT_ERROR, 0);
255 if (events[index].revents & POLLERR)
258 int fd = events[index].fd;
261 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
263 eh->HandleEvent(EVENT_ERROR, errcode);
267 if (events[index].revents & POLLIN)
269 SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
270 eh->HandleEvent(EVENT_READ);
271 if (eh != ref[index])
272 // whoops, deleted out from under us
276 if (events[index].revents & POLLOUT)
278 int mask = eh->GetEventMask();
279 mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
280 SetEventMask(eh, mask);
281 events[index].events = mask_to_poll(mask);
282 eh->HandleEvent(EVENT_WRITE);
290 std::string PollEngine::GetName()
295 SocketEngine* CreateSocketEngine()
297 return new PollEngine;