]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_kqueue.cpp
Merge insp20
[user/henk/code/inspircd.git] / src / socketengines / socketengine_kqueue.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
6  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "exitcodes.h"
24 #include <sys/types.h>
25 #include <sys/event.h>
26 #include <sys/time.h>
27 #include "socketengine.h"
28 #include <iostream>
29
30 /** A specialisation of the SocketEngine class, designed to use BSD kqueue().
31  */
32 class KQueueEngine : public SocketEngine
33 {
34 private:
35         int EngineHandle;
36         /** These are used by kqueue() to hold socket events
37          */
38         struct kevent* ke_list;
39         /** This is a specialised time value used by kqueue()
40          */
41         struct timespec ts;
42 public:
43         /** Create a new KQueueEngine
44          */
45         KQueueEngine();
46         /** Delete a KQueueEngine
47          */
48         virtual ~KQueueEngine();
49         bool AddFd(EventHandler* eh, int event_mask);
50         void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
51         virtual void DelFd(EventHandler* eh);
52         virtual int DispatchEvents();
53         virtual std::string GetName();
54         virtual void RecoverFromFork();
55 };
56
57 #include <sys/sysctl.h>
58
59 KQueueEngine::KQueueEngine()
60 {
61         MAX_DESCRIPTORS = 0;
62         int mib[2];
63         size_t len;
64
65         mib[0] = CTL_KERN;
66 #ifdef KERN_MAXFILESPERPROC
67         mib[1] = KERN_MAXFILESPERPROC;
68 #else
69         mib[1] = KERN_MAXFILES;
70 #endif
71         len = sizeof(MAX_DESCRIPTORS);
72         sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
73         if (MAX_DESCRIPTORS <= 0)
74         {
75                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
76                 std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
77                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
78         }
79
80         this->RecoverFromFork();
81         ke_list = new struct kevent[GetMaxFds()];
82         ref = new EventHandler* [GetMaxFds()];
83         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
84 }
85
86 void KQueueEngine::RecoverFromFork()
87 {
88         /*
89          * The only bad thing about kqueue is that its fd cant survive a fork and is not inherited.
90          * BUM HATS.
91          *
92          */
93         EngineHandle = kqueue();
94         if (EngineHandle == -1)
95         {
96                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
97                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: this is a fatal error, exiting now.");
98                 std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl;
99                 std::cout << "ERROR: this is a fatal error, exiting now." << std::endl;
100                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
101         }
102         CurrentSetSize = 0;
103 }
104
105 KQueueEngine::~KQueueEngine()
106 {
107         this->Close(EngineHandle);
108         delete[] ref;
109         delete[] ke_list;
110 }
111
112 bool KQueueEngine::AddFd(EventHandler* eh, int event_mask)
113 {
114         int fd = eh->GetFd();
115
116         if ((fd < 0) || (fd > GetMaxFds() - 1))
117                 return false;
118
119         if (ref[fd])
120                 return false;
121
122         // We always want to read from the socket...
123         struct kevent ke;
124         EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
125
126         int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
127         if (i == -1)
128         {
129                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to add fd: %d %s",
130                                           fd, strerror(errno));
131                 return false;
132         }
133
134         ref[fd] = eh;
135         SocketEngine::SetEventMask(eh, event_mask);
136         OnSetEvent(eh, 0, event_mask);
137         CurrentSetSize++;
138
139         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
140         return true;
141 }
142
143 void KQueueEngine::DelFd(EventHandler* eh)
144 {
145         int fd = eh->GetFd();
146
147         if ((fd < 0) || (fd > GetMaxFds() - 1))
148         {
149                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd);
150                 return;
151         }
152
153         struct kevent ke;
154
155         // First remove the write filter ignoring errors, since we can't be
156         // sure if there are actually any write filters registered.
157         EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
158         kevent(EngineHandle, &ke, 1, 0, 0, NULL);
159
160         // Then remove the read filter.
161         EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
162         int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
163
164         if (j < 0)
165         {
166                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to remove fd: %d %s",
167                                           fd, strerror(errno));
168         }
169
170         CurrentSetSize--;
171         ref[fd] = NULL;
172
173         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
174 }
175
176 void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
177 {
178         if ((new_mask & FD_WANT_POLL_WRITE) && !(old_mask & FD_WANT_POLL_WRITE))
179         {
180                 // new poll-style write
181                 struct kevent ke;
182                 EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
183                 int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
184                 if (i < 0) {
185                         ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
186                                                   eh->GetFd(), strerror(errno));
187                 }
188         }
189         else if ((old_mask & FD_WANT_POLL_WRITE) && !(new_mask & FD_WANT_POLL_WRITE))
190         {
191                 // removing poll-style write
192                 struct kevent ke;
193                 EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
194                 int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
195                 if (i < 0) {
196                         ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
197                                                   eh->GetFd(), strerror(errno));
198                 }
199         }
200         if ((new_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !(old_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)))
201         {
202                 // new one-shot write
203                 struct kevent ke;
204                 EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
205                 int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
206                 if (i < 0) {
207                         ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
208                                                   eh->GetFd(), strerror(errno));
209                 }
210         }
211 }
212
213 int KQueueEngine::DispatchEvents()
214 {
215         ts.tv_nsec = 0;
216         ts.tv_sec = 1;
217
218         int i = kevent(EngineHandle, NULL, 0, &ke_list[0], GetMaxFds(), &ts);
219         ServerInstance->UpdateTime();
220
221         TotalEvents += i;
222
223         for (int j = 0; j < i; j++)
224         {
225                 EventHandler* eh = ref[ke_list[j].ident];
226                 if (!eh)
227                         continue;
228                 if (ke_list[j].flags & EV_EOF)
229                 {
230                         ErrorEvents++;
231                         eh->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
232                         continue;
233                 }
234                 if (ke_list[j].filter == EVFILT_WRITE)
235                 {
236                         WriteEvents++;
237                         /* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE,
238                          * we set a one-shot write, so we need to clear that bit
239                          * to detect when it set again.
240                          */
241                         const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
242                         SetEventMask(eh, eh->GetEventMask() & ~bits_to_clr);
243                         eh->HandleEvent(EVENT_WRITE);
244
245                         if (eh != ref[ke_list[j].ident])
246                                 // whoops, deleted out from under us
247                                 continue;
248                 }
249                 if (ke_list[j].filter == EVFILT_READ)
250                 {
251                         ReadEvents++;
252                         SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
253                         eh->HandleEvent(EVENT_READ);
254                 }
255         }
256
257         return i;
258 }
259
260 std::string KQueueEngine::GetName()
261 {
262         return "kqueue";
263 }
264
265 SocketEngine* CreateSocketEngine()
266 {
267         return new KQueueEngine;
268 }