]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_kqueue.cpp
Dump sendq before closing socket
[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 bool DelFd(EventHandler* eh, bool force = false);
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_MAXFILES;
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 bool KQueueEngine::DelFd(EventHandler* eh, bool force)
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 false;
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) && !force)
152         {
153                 ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
154                                           fd, strerror(errno));
155                 return false;
156         }
157
158         CurrentSetSize--;
159         ref[fd] = NULL;
160
161         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
162         return true;
163 }
164
165 void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
166 {
167         if ((new_mask & FD_WANT_POLL_WRITE) && !(old_mask & FD_WANT_POLL_WRITE))
168         {
169                 // new poll-style write
170                 struct kevent ke;
171                 EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
172                 int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
173                 if (i < 0) {
174                         ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
175                                                   eh->GetFd(), strerror(errno));
176                 }
177         }
178         else if ((old_mask & FD_WANT_POLL_WRITE) && !(new_mask & FD_WANT_POLL_WRITE))
179         {
180                 // removing poll-style write
181                 struct kevent ke;
182                 EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
183                 int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
184                 if (i < 0) {
185                         ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
186                                                   eh->GetFd(), strerror(errno));
187                 }
188         }
189         if ((new_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !(old_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)))
190         {
191                 // new one-shot write
192                 struct kevent ke;
193                 EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
194                 int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
195                 if (i < 0) {
196                         ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
197                                                   eh->GetFd(), strerror(errno));
198                 }
199         }
200 }
201
202 int KQueueEngine::DispatchEvents()
203 {
204         ts.tv_nsec = 0;
205         ts.tv_sec = 1;
206
207         int i = kevent(EngineHandle, NULL, 0, &ke_list[0], GetMaxFds(), &ts);
208         ServerInstance->UpdateTime();
209
210         TotalEvents += i;
211
212         for (int j = 0; j < i; j++)
213         {
214                 EventHandler* eh = ref[ke_list[j].ident];
215                 if (!eh)
216                         continue;
217                 if (ke_list[j].flags & EV_EOF)
218                 {
219                         ErrorEvents++;
220                         eh->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
221                         continue;
222                 }
223                 if (ke_list[j].filter == EVFILT_WRITE)
224                 {
225                         WriteEvents++;
226                         /* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE,
227                          * we set a one-shot write, so we need to clear that bit
228                          * to detect when it set again.
229                          */
230                         const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
231                         SetEventMask(eh, eh->GetEventMask() & ~bits_to_clr);
232                         eh->HandleEvent(EVENT_WRITE);
233                 }
234                 if (ke_list[j].filter == EVFILT_READ)
235                 {
236                         ReadEvents++;
237                         SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
238                         eh->HandleEvent(EVENT_READ);
239                 }
240         }
241
242         return i;
243 }
244
245 std::string KQueueEngine::GetName()
246 {
247         return "kqueue";
248 }
249
250 SocketEngine* CreateSocketEngine()
251 {
252         return new KQueueEngine;
253 }