]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_iocp.cpp
Keep count of the number of events in total, and seperate read, write and error event...
[user/henk/code/inspircd.git] / src / socketengines / socketengine_iocp.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "socketengines/socketengine_iocp.h"
15 #include "exitcodes.h"
16 #include <mswsock.h>
17
18 IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
19 {
20         /* Create completion port */
21         m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
22
23         if (!m_completionPort)
24         {
25                 ServerInstance->Log(DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
26                 ServerInstance->Log(DEFAULT, "ERROR: this is a fatal error, exiting now.");
27                 printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.\n");
28                 printf("ERROR: this is a fatal error, exiting now.\n");
29                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
30         }
31
32         /* Null variables out. */
33         CurrentSetSize = 0;
34         EngineHandle = 0;
35         memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
36 }
37
38 IOCPEngine::~IOCPEngine()
39 {
40         /* Clean up winsock and close completion port */
41         CloseHandle(m_completionPort);
42         WSACleanup();
43 }
44
45 bool IOCPEngine::AddFd(EventHandler* eh)
46 {
47         /* Does it at least look valid? */
48         if (!eh)
49                 return false;
50
51         int* fake_fd = new int(GenerateFd(eh->GetFd()));
52         int is_accept = 0;
53         int opt_len = sizeof(int);
54
55         /* In range? */
56         if ((*fake_fd < 0) || (*fake_fd > MAX_DESCRIPTORS))
57         {
58                 delete fake_fd;
59                 return false;
60         }
61
62         /* Already an entry here */
63         if (ref[*fake_fd])
64         {
65                 delete fake_fd;
66                 return false;
67         }
68
69         /* are we a listen socket? */
70         getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
71
72         /* set up the read event so the socket can actually receive data :P */
73         eh->Extend("internal_fd", fake_fd);
74
75         unsigned long completion_key = (ULONG_PTR)*fake_fd;
76         /* assign the socket to the completion port */
77         if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
78                 return false;
79
80         /* setup initial events */
81         if(is_accept)
82                 PostAcceptEvent(eh);
83         else
84                 PostReadEvent(eh);
85
86         /* log message */
87         ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
88
89         /* post a write event if there is data to be written */
90         if(eh->Writeable())
91                 WantWrite(eh);
92
93         /* we're all good =) */
94         try
95         {
96                 m_binding.insert( std::map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
97         }
98         catch (...)
99         {
100                 /* Ohshi-, map::insert failed :/ */
101                 return false;
102         }
103
104         ++CurrentSetSize;
105         ref[*fake_fd] = eh;
106
107         return true;
108 }
109
110 bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
111 {
112         if (!eh)
113                 return false;
114
115         int* fake_fd = NULL;
116
117         if (!eh->GetExt("internal_fd", fake_fd))
118                 return false;
119
120         int fd = eh->GetFd();
121
122         void* m_readEvent = NULL;
123         void* m_writeEvent = NULL;
124         void* m_acceptEvent = NULL;
125
126         ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
127
128         /* Cancel pending i/o operations. */
129         if (CancelIo((HANDLE)fd) == FALSE)
130                 return false;
131
132         /* Free the buffer, and delete the event. */
133         if (eh->GetExt("windows_readevent", m_readEvent))
134         {
135                 if(((Overlapped*)m_readEvent)->m_params != 0)
136                         delete ((udp_overlap*)((Overlapped*)m_readEvent)->m_params);
137
138                 delete ((Overlapped*)m_readEvent);
139                 eh->Shrink("windows_readevent");
140         }
141
142         if(eh->GetExt("windows_writeevent", m_writeEvent))
143         {
144                 delete ((Overlapped*)m_writeEvent);
145                 eh->Shrink("windows_writeevent");
146         }
147
148         if(eh->GetExt("windows_acceptevent", m_acceptEvent))
149         {
150                 delete ((accept_overlap*)((Overlapped*)m_acceptEvent)->m_params);
151                 delete ((Overlapped*)m_acceptEvent);
152                 eh->Shrink("windows_accepevent");
153         }
154
155         /* Clear binding */
156         ref[*fake_fd] = 0;
157         m_binding.erase(eh->GetFd());
158
159         delete fake_fd;
160         eh->Shrink("internal_fd");
161
162         /* decrement set size */
163         --CurrentSetSize;
164         
165         /* success */
166         return true;
167 }
168
169 void IOCPEngine::WantWrite(EventHandler* eh)
170 {
171         if (!eh)
172                 return;
173         
174         void* m_writeEvent = NULL;
175
176         int* fake_fd = NULL;
177         if (!eh->GetExt("internal_fd", fake_fd))
178                 return;
179
180         /* Post event - write begin */
181         if(!eh->GetExt("windows_writeevent", m_writeEvent))
182         {
183                 ULONG_PTR completion_key = (ULONG_PTR)*fake_fd;
184                 Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
185                 eh->Shrink("windows_writeevent");
186                 eh->Extend("windows_writeevent",ov);
187                 PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
188         }
189 }
190
191 bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
192 {
193         if (!eh)
194                 return false;
195
196         int* fake_fd = NULL;
197         if (!eh->GetExt("internal_fd", fake_fd))
198                 return false;
199
200         Overlapped * ov = new Overlapped(type, param);
201         ULONG_PTR completion_key = (ULONG_PTR)*fake_fd;
202         return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
203 }
204
205 void IOCPEngine::PostReadEvent(EventHandler * eh)
206 {
207         if (!eh)
208                 return;
209
210         Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
211         DWORD flags = 0;
212         DWORD r_length = 0;
213         WSABUF buf;
214
215         /* by passing a null buffer pointer, we can have this working in the same way as epoll..
216          * its slower, but it saves modifying all network code.
217          */
218         buf.buf = 0;
219         buf.len = 0;
220
221         /* determine socket type. */
222         DWORD sock_type;
223         int sock_len = sizeof(DWORD);
224         if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
225         {
226                 /* wtfhax? */
227                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
228                 delete ov;
229                 return;
230         }
231         switch(sock_type)
232         {
233                 case SOCK_DGRAM:                        /* UDP Socket */
234                 {
235                         udp_overlap * uv = new udp_overlap;
236                         uv->udp_sockaddr_len = sizeof(sockaddr);
237                         buf.buf = (char*)uv->udp_buffer;
238                         buf.len = sizeof(uv->udp_buffer);
239                         ov->m_params = (unsigned long)uv;
240                         if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
241                         {
242                                 int err = WSAGetLastError();
243                                 if(err != WSA_IO_PENDING)
244                                 {
245                                         delete ov;
246                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
247                                         return;
248                                 }
249                         }
250                 }
251                 break;
252
253                 case SOCK_STREAM:                       /* TCP Socket */
254                 {
255                         if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
256                         {
257                                 if(WSAGetLastError() != WSA_IO_PENDING)
258                                 {
259                                         delete ov;
260                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
261                                         return;
262                                 }
263                         }
264                 }
265                 break;
266
267                 default:
268                 {
269                         printf("unknwon socket type: %u\n", sock_type);
270                         return;
271                 }
272                 break;
273         }
274         eh->Extend("windows_readevent", ov);
275 }
276
277 int IOCPEngine::DispatchEvents()
278 {
279         DWORD len;
280         LPOVERLAPPED overlap;
281         Overlapped * ov;
282         EventHandler * eh;
283         ULONG_PTR intfd;
284         int ret;
285         unsigned long bytes_recv;
286
287         while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
288         {
289                 if (intfd < 0 || intfd > MAX_DESCRIPTORS)
290                         continue;
291
292                 // woot, we got an event on a socket :P
293                 eh = ref[intfd];
294                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
295
296                 if (eh == 0)
297                         continue;
298
299                 void* m_readEvent = NULL;
300                 void* m_writeEvent = NULL;
301
302                 eh->GetExt("windows_readevent", m_readEvent);
303                 eh->GetExt("windows_writeevent", m_writeEvent);
304
305                 TotalEvents++;
306
307                 switch(ov->m_event)
308                 {
309                         case SOCKET_IO_EVENT_WRITE_READY:
310                         {
311                                 WriteEvents++;
312                                 eh->Shrink("windows_writeevent");
313                                 eh->HandleEvent(EVENT_WRITE, 0);
314                         }
315                         break;
316
317                         case SOCKET_IO_EVENT_READ_READY:
318                         {
319                                 ReadEvents++;
320                                 if(ov->m_params)
321                                 {
322                                         // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
323                                         udp_overlap * uv = (udp_overlap*)ov->m_params;
324                                         uv->udp_len = len;
325                                         this->udp_ov = uv;
326                                         eh->Shrink("windows_readevent");
327                                         eh->HandleEvent(EVENT_READ, 0);
328                                         this->udp_ov = 0;
329                                         delete uv;
330                                         PostReadEvent(eh);
331                                 }
332                                 else
333                                 {
334                                         ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
335                                         eh->Shrink("windows_readevent");
336                                         if(ret != 0 || bytes_recv == 0)
337                                         {
338                                                 /* end of file */
339                                                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
340                                         }
341                                         else
342                                         {
343                                                 eh->HandleEvent(EVENT_READ, 0);
344                                                 PostReadEvent(eh);
345                                         }
346                                 }
347                         }
348                         break;
349                 
350                         case SOCKET_IO_EVENT_ACCEPT:
351                         {
352                                 /* this is kinda messy.. :/ */
353                                 ReadEvents++;
354                                 eh->HandleEvent(EVENT_READ, ov->m_params);
355                                 delete ((accept_overlap*)ov->m_params);
356                                 eh->Shrink("windows_acceptevent");
357                                 PostAcceptEvent(eh);
358                         }
359                         break;
360
361                         case SOCKET_IO_EVENT_ERROR:
362                         {
363                                 ErrorEvents++;
364                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
365                         }
366                         break;
367                 }
368                 
369                 delete ov;
370         }
371
372         return 0;
373 }
374
375 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
376 {
377         if (!eh)
378                 return;
379
380         int on = 1;
381         u_long arg = 1;
382         struct linger linger = { 0 };
383
384         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
385
386         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
387         /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
388         linger.l_onoff = 1;
389         linger.l_linger = 1;
390         setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
391         ioctlsocket(fd, FIONBIO, &arg);
392
393         int len = sizeof(sockaddr_in) + 16;
394         DWORD dwBytes;
395         accept_overlap* ao = new accept_overlap;
396         memset(ao->buf, 0, 1024);
397         ao->socket = fd;
398
399         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
400         eh->Extend("windows_acceptevent", ov);
401
402         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
403         {
404                 int err = WSAGetLastError();
405                 if(err != WSA_IO_PENDING)
406                 {
407                         printf("PostAcceptEvent err: %d\n", err);
408                 }
409         }
410 }
411
412
413 std::string IOCPEngine::GetName()
414 {
415         return "iocp";
416 }
417
418 EventHandler * IOCPEngine::GetRef(int fd)
419 {
420         std::map<int, EventHandler*>::iterator itr = m_binding.find(fd);
421         return (itr == m_binding.end()) ? 0 : itr->second;
422 }
423
424 bool IOCPEngine::HasFd(int fd)
425 {
426         return (GetRef(fd) != 0);
427 }
428
429 bool IOCPEngine::BoundsCheckFd(EventHandler* eh)
430 {
431         int * internal_fd;
432         if (!eh || eh->GetFd() < 0)
433                 return false;
434
435         if(!eh->GetExt("internal_fd", internal_fd))
436                 return false;
437
438         if(*internal_fd > MAX_DESCRIPTORS)
439                 return false;
440
441         return true;
442 }
443
444 EventHandler * IOCPEngine::GetIntRef(int fd)
445 {
446         if(fd < 0 || fd > MAX_DESCRIPTORS)
447                 return 0;
448         return ref[fd];
449 }
450
451 int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen)
452 {
453         SOCKET s = fd->GetFd();
454
455         Overlapped* acceptevent = NULL;
456         if (!fd->GetExt("windows_acceptevent", acceptevent))
457                 /* Shit, no accept event on this socket! :( */
458                 return -1;
459
460         Overlapped* ovl = acceptevent;
461         accept_overlap* ov = (accept_overlap*)ovl->m_params;
462         
463         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
464         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
465
466         memcpy(addr, client_address, sizeof(sockaddr_in));
467         *addrlen = sizeof(sockaddr_in);
468
469         return ov->socket;
470 }
471
472 int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen)
473 {
474         Overlapped* ovl = NULL;
475         
476         if (!fd->GetExt("windows_acceptevent", ovl))
477                 return -1;
478
479         accept_overlap* ov = (accept_overlap*)ovl->m_params;
480
481         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
482         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
483
484         memcpy(name, server_address, sizeof(sockaddr_in));
485         *namelen = sizeof(sockaddr_in);
486
487         return 0;
488 }
489
490 int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
491 {
492         udp_overlap * ov = NULL;
493         if (!fd->GetExt("windows_readevent", ov))
494                 return -1;
495         memcpy(buf, ov->udp_buffer, ov->udp_len);
496         memcpy(from, ov->udp_sockaddr, *fromlen);
497         return ov->udp_len;
498 }
499
500 int IOCPEngine::Blocking(int fd)
501 {
502         unsigned long opt = 0;
503         return ioctlsocket(fd, FIONBIO, &opt);
504 }
505
506 int IOCPEngine::NonBlocking(int fd)
507 {
508         unsigned long opt = 1;
509         return ioctlsocket(fd, FIONBIO, &opt);
510 }
511
512 int IOCPEngine::Close(int fd)
513 {
514         return closesocket(fd);
515 }
516
517 int IOCPEngine::Close(EventHandler* fd)
518 {
519         return this->Close(fd->GetFd());
520 }
521