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