1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2010 InspIRCd Development Team
6 * See: http://wiki.inspircd.org/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
15 #include "exitcodes.h"
17 #ifndef __SOCKETENGINE_POLL__
18 #define __SOCKETENGINE_POLL__
23 #include "inspircd_config.h"
25 #include "socketengine.h"
29 #define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
35 #define struct pollfd WSAPOLLFD
41 /** A specialisation of the SocketEngine class, designed to use poll().
43 class PollEngine : public SocketEngine
46 /** These are used by poll() to hold socket events
48 struct pollfd *events;
49 /** This map maps fds to an index in the events array.
51 std::map<int, unsigned int> fd_mappings;
53 /** Create a new PollEngine
56 /** Delete a PollEngine
58 virtual ~PollEngine();
59 virtual bool AddFd(EventHandler* eh, int event_mask);
60 virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
61 virtual EventHandler* GetRef(int fd);
62 virtual void DelFd(EventHandler* eh);
63 virtual int DispatchEvents();
64 virtual std::string GetName();
71 #include <sys/sysctl.h>
74 PollEngine::PollEngine()
78 int max = ulimit(4, 0);
81 MAX_DESCRIPTORS = max;
85 ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
86 printf("ERROR: Can't determine maximum number of open sockets: %s\n", strerror(errno));
87 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
94 mib[1] = KERN_MAXFILES;
95 len = sizeof(MAX_DESCRIPTORS);
96 sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
99 ref = new EventHandler* [GetMaxFds()];
100 events = new struct pollfd[GetMaxFds()];
102 memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
103 memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
106 PollEngine::~PollEngine()
108 // No destruction required, either.
113 static int mask_to_poll(int event_mask)
116 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
118 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
123 bool PollEngine::AddFd(EventHandler* eh, int event_mask)
125 int fd = eh->GetFd();
126 if ((fd < 0) || (fd > GetMaxFds() - 1))
128 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
132 if (fd_mappings.find(fd) != fd_mappings.end())
134 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
138 unsigned int index = CurrentSetSize;
140 fd_mappings[fd] = index;
142 events[index].fd = fd;
143 events[index].events = mask_to_poll(event_mask);
145 ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
146 SocketEngine::SetEventMask(eh, event_mask);
151 EventHandler* PollEngine::GetRef(int fd)
153 std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
154 if (it == fd_mappings.end())
156 return ref[it->second];
159 void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
161 std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
162 if (it == fd_mappings.end())
164 ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd());
168 events[it->second].events = mask_to_poll(new_mask);
171 void PollEngine::DelFd(EventHandler* eh)
173 int fd = eh->GetFd();
174 if ((fd < 0) || (fd > MAX_DESCRIPTORS))
176 ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
180 std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
181 if (it == fd_mappings.end())
183 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
187 unsigned int index = it->second;
188 unsigned int last_index = CurrentSetSize - 1;
189 int last_fd = events[last_index].fd;
191 if (index != last_index)
193 // We need to move the last fd we got into this gap (gaps are evil!)
195 // So update the mapping for the last fd to its new position
196 fd_mappings[last_fd] = index;
198 // move last_fd from last_index into index
199 events[index].fd = last_fd;
200 events[index].events = events[last_index].events;
202 ref[index] = ref[last_index];
205 // Now remove all data for the last fd we got into out list.
206 // Above code made sure this always is right
207 fd_mappings.erase(it);
208 events[last_index].fd = 0;
209 events[last_index].events = 0;
210 ref[last_index] = NULL;
214 ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
215 "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
218 int PollEngine::DispatchEvents()
220 int i = poll(events, CurrentSetSize, 1000);
222 socklen_t codesize = sizeof(int);
225 ServerInstance->UpdateTime();
229 for (index = 0; index < CurrentSetSize && processed != i; index++)
231 if (events[index].revents)
233 EventHandler* eh = ref[index];
237 if (events[index].revents & POLLHUP)
239 eh->HandleEvent(EVENT_ERROR, 0);
243 if (events[index].revents & POLLERR)
246 int fd = events[index].fd;
249 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
251 eh->HandleEvent(EVENT_ERROR, errcode);
255 if (events[index].revents & POLLIN)
257 SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
258 eh->HandleEvent(EVENT_READ);
259 if (eh != ref[index])
260 // whoops, deleted out from under us
264 if (events[index].revents & POLLOUT)
266 int mask = eh->GetEventMask();
267 mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
268 SetEventMask(eh, mask);
269 events[index].events = mask_to_poll(mask);
270 eh->HandleEvent(EVENT_WRITE);
278 std::string PollEngine::GetName()
283 SocketEngine* CreateSocketEngine()
285 return new PollEngine;