]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
Remove include/inspircd_se_config.h and socketengine-specific headers
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include <vector>
15 #include <string>
16 #include <map>
17 #include "inspircd.h"
18 #include "exitcodes.h"
19 #include "socketengine.h"
20 #include <sys/epoll.h>
21 #include <ulimit.h>
22 #define EP_DELAY 5
23
24 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
25  */
26 class EPollEngine : public SocketEngine
27 {
28 private:
29         /** These are used by epoll() to hold socket events
30          */
31         struct epoll_event* events;
32         int EngineHandle;
33 public:
34         /** Create a new EPollEngine
35          */
36         EPollEngine();
37         /** Delete an EPollEngine
38          */
39         virtual ~EPollEngine();
40         virtual bool AddFd(EventHandler* eh, int event_mask);
41         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
42         virtual bool DelFd(EventHandler* eh, bool force = false);
43         virtual int DispatchEvents();
44         virtual std::string GetName();
45 };
46
47 EPollEngine::EPollEngine()
48 {
49         int max = ulimit(4, 0);
50         if (max > 0)
51         {
52                 MAX_DESCRIPTORS = max;
53         }
54         else
55         {
56                 ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
57                 printf("ERROR: Can't determine maximum number of open sockets!\n");
58                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
59         }
60
61         // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
62         EngineHandle = epoll_create(GetMaxFds() / 4);
63
64         if (EngineHandle == -1)
65         {
66                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
67                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
68                 printf("ERROR: Could not initialize epoll socket engine: %s\n", strerror(errno));
69                 printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n");
70                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
71         }
72
73         ref = new EventHandler* [GetMaxFds()];
74         events = new struct epoll_event[GetMaxFds()];
75
76         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
77 }
78
79 EPollEngine::~EPollEngine()
80 {
81         this->Close(EngineHandle);
82         delete[] ref;
83         delete[] events;
84 }
85
86 static int mask_to_epoll(int event_mask)
87 {
88         int rv = 0;
89         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
90         {
91                 // we need to use standard polling on this FD
92                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
93                         rv |= EPOLLIN;
94                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
95                         rv |= EPOLLOUT;
96         }
97         else
98         {
99                 // we can use edge-triggered polling on this FD
100                 rv = EPOLLET;
101                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
102                         rv |= EPOLLIN;
103                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
104                         rv |= EPOLLOUT;
105         }
106         return rv;
107 }
108
109 bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
110 {
111         int fd = eh->GetFd();
112         if ((fd < 0) || (fd > GetMaxFds() - 1))
113         {
114                 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
115                 return false;
116         }
117
118         if (ref[fd])
119         {
120                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
121                 return false;
122         }
123
124         struct epoll_event ev;
125         memset(&ev,0,sizeof(ev));
126         ev.events = mask_to_epoll(event_mask);
127         ev.data.fd = fd;
128         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
129         if (i < 0)
130         {
131                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
132                 return false;
133         }
134
135         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
136
137         ref[fd] = eh;
138         SocketEngine::SetEventMask(eh, event_mask);
139         CurrentSetSize++;
140         return true;
141 }
142
143 void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
144 {
145         int old_events = mask_to_epoll(old_mask);
146         int new_events = mask_to_epoll(new_mask);
147         if (old_events != new_events)
148         {
149                 // ok, we actually have something to tell the kernel about
150                 struct epoll_event ev;
151                 memset(&ev,0,sizeof(ev));
152                 ev.events = new_events;
153                 ev.data.fd = eh->GetFd();
154                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
155         }
156 }
157
158 bool EPollEngine::DelFd(EventHandler* eh, bool force)
159 {
160         int fd = eh->GetFd();
161         if ((fd < 0) || (fd > GetMaxFds() - 1))
162         {
163                 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
164                 return false;
165         }
166
167         struct epoll_event ev;
168         memset(&ev,0,sizeof(ev));
169         ev.data.fd = fd;
170         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
171
172         if (i < 0 && !force)
173         {
174                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Cant remove socket: %s", strerror(errno));
175                 return false;
176         }
177
178         ref[fd] = NULL;
179
180         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
181         CurrentSetSize--;
182         return true;
183 }
184
185 int EPollEngine::DispatchEvents()
186 {
187         socklen_t codesize = sizeof(int);
188         int errcode;
189         int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
190
191         TotalEvents += i;
192
193         for (int j = 0; j < i; j++)
194         {
195                 EventHandler* eh = ref[events[j].data.fd];
196                 if (!eh)
197                         continue;
198                 if (events[j].events & EPOLLHUP)
199                 {
200                         ErrorEvents++;
201                         eh->HandleEvent(EVENT_ERROR, 0);
202                         continue;
203                 }
204                 if (events[j].events & EPOLLERR)
205                 {
206                         ErrorEvents++;
207                         /* Get error number */
208                         if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
209                                 errcode = errno;
210                         eh->HandleEvent(EVENT_ERROR, errcode);
211                         continue;
212                 }
213                 int mask = eh->GetEventMask();
214                 if (events[j].events & EPOLLIN)
215                         mask &= ~FD_READ_WILL_BLOCK;
216                 if (events[j].events & EPOLLOUT)
217                 {
218                         mask &= ~FD_WRITE_WILL_BLOCK;
219                         if (mask & FD_WANT_SINGLE_WRITE)
220                         {
221                                 int nm = mask & ~FD_WANT_SINGLE_WRITE;
222                                 OnSetEvent(eh, mask, nm);
223                                 mask = nm;
224                         }
225                 }
226                 SetEventMask(eh, mask);
227                 if (events[j].events & EPOLLIN)
228                 {
229                         ReadEvents++;
230                         eh->HandleEvent(EVENT_READ);
231                 }
232                 if (events[j].events & EPOLLOUT)
233                 {
234                         WriteEvents++;
235                         eh->HandleEvent(EVENT_WRITE);
236                 }
237         }
238
239         return i;
240 }
241
242 std::string EPollEngine::GetName()
243 {
244         return "epoll";
245 }
246
247 SocketEngine* CreateSocketEngine()
248 {
249         return new EPollEngine;
250 }