]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengine_iocp.cpp
Extra Dry! Shame it's not in Hereford right now :p
[user/henk/code/inspircd.git] / src / socketengine_iocp.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 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 "socketengine_iocp.h"
15 #include <mswsock.h>
16
17 IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
18 {
19         /* Create completion port */
20         m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
21
22         if (m_completionPort == -1)
23         {
24                 ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
25                 ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now.");
26                 printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
27                 printf("ERROR: this is a fatal error, exiting now.");
28                 InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
29         }
30
31         /* Null variables out. */
32         CurrentSetSize = 0;
33         EngineHandle = 0;
34         memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
35 }
36
37 IOCPEngine::~IOCPEngine()
38 {
39         CloseHandle(m_completionPort);
40 }
41
42 bool IOCPEngine::AddFd(EventHandler* eh)
43 {
44         /* Does it at least look valid? */
45         if (!eh)
46                 return false;
47
48         int fake_fd = GenerateFd(eh->GetFd());
49         int is_accept = 0;
50         int opt_len = sizeof(int);
51
52         /* In range? */
53         if ((fake_fd < 0) || (fake_fd > MAX_DESCRIPTOR))
54                 return false;
55
56         /* Already an entry here */
57         if (ref[fake_fd])
58                 return false;
59
60         /* are we a listen socket? */
61         getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
62
63         /* set up the read event so the socket can actually receive data :P */
64         eh->m_internalFd = fake_fd;
65         eh->m_writeEvent = 0;
66         eh->m_acceptEvent = 0;
67
68         unsigned long completion_key = (ULONG_PTR)eh->m_internalFd;
69         /* assign the socket to the completion port */
70         if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
71                 return false;
72
73         /* set up binding, increase set size */
74         ref[fake_fd] = eh;
75         ++CurrentSetSize;
76
77         /* setup initial events */
78         if(is_accept)
79                 PostAcceptEvent(eh);
80         else
81                 PostReadEvent(eh);
82
83         /* log message */
84         ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh);
85
86         /* post a write event if there is data to be written */
87         if(eh->Writeable())
88                 WantWrite(eh);
89
90         /* we're all good =) */
91         try
92         {
93                 m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
94         }
95         catch (...)
96         {
97                 /* Ohshi-, map::insert failed :/ */
98                 return false;
99         }
100
101         return true;
102 }
103
104 bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
105 {
106         if (!eh)
107                 return false;
108
109         int fake_fd = eh->m_internalFd;
110         int fd = eh->GetFd();
111         
112         if (!ref[fake_fd])
113                 return false;
114
115         ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh);
116
117         /* Cancel pending i/o operations. */
118         if (CancelIo((HANDLE)fd) == FALSE)
119                 return false;
120
121         /* Free the buffer, and delete the event. */
122         if (eh->m_readEvent != 0)
123         {
124                 if(((Overlapped*)eh->m_readEvent)->m_params != 0)
125                         delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params);
126
127                 delete ((Overlapped*)eh->m_readEvent);
128         }
129
130         if(eh->m_writeEvent != 0)
131                 delete ((Overlapped*)eh->m_writeEvent);
132
133         if(eh->m_acceptEvent != 0)
134         {
135                 delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params);
136                 delete ((Overlapped*)eh->m_acceptEvent);
137         }
138
139         /* Clear binding */
140         ref[fake_fd] = 0;
141         m_binding.erase(eh->GetFd());
142
143         /* decrement set size */
144         --CurrentSetSize;
145         
146         /* success */
147         return true;
148 }
149
150 void IOCPEngine::WantWrite(EventHandler* eh)
151 {
152         if (!eh)
153                 return;
154
155         /* Post event - write begin */
156         if(!eh->m_writeEvent)
157         {
158                 ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
159                 Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
160                 eh->m_writeEvent = (void*)ov;
161                 PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
162         }
163 }
164
165 bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
166 {
167         if (!eh)
168                 return false;
169
170         Overlapped * ov = new Overlapped(type, param);
171         ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
172         return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
173 }
174
175 void IOCPEngine::PostReadEvent(EventHandler * eh)
176 {
177         if (!eh)
178                 return false;
179
180         Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
181         DWORD flags = 0;
182         DWORD r_length = 0;
183         WSABUF buf;
184
185         /* by passing a null buffer pointer, we can have this working in the same way as epoll..
186          * its slower, but it saves modifying all network code.
187          */
188         buf.buf = 0;
189         buf.len = 0;
190
191         /* determine socket type. */
192         DWORD sock_type;
193         int sock_len = sizeof(DWORD);
194         if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
195         {
196                 /* wtfhax? */
197                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
198                 delete ov;
199                 return;
200         }
201         switch(sock_type)
202         {
203                 case SOCK_DGRAM:                        /* UDP Socket */
204                 {
205                         udp_overlap * uv = new udp_overlap;
206                         uv->udp_sockaddr_len = sizeof(sockaddr);
207                         buf.buf = (char*)uv->udp_buffer;
208                         buf.len = sizeof(uv->udp_buffer);
209                         ov->m_params = (unsigned long)uv;
210                         if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
211                         {
212                                 int err = WSAGetLastError();
213                                 if(err != WSA_IO_PENDING)
214                                 {
215                                         delete ov;
216                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
217                                         return;
218                                 }
219                         }
220                 }
221                 break;
222
223                 case SOCK_STREAM:                       /* TCP Socket */
224                 {
225                         if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
226                         {
227                                 if(WSAGetLastError() != WSA_IO_PENDING)
228                                 {
229                                         delete ov;
230                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
231                                         return;
232                                 }
233                         }
234                 }
235                 break;
236
237                 default:
238                 {
239                         printf("unknwon socket type: %u\n", sock_type);
240                         return;
241                 }
242                 break;
243         }
244         eh->m_readEvent = (void*)ov;
245 }
246
247 int IOCPEngine::DispatchEvents()
248 {
249         DWORD len;
250         LPOVERLAPPED overlap;
251         Overlapped * ov;
252         EventHandler * eh;
253         ULONG_PTR intfd;
254         int ret;
255         unsigned long bytes_recv;
256
257         while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
258         {
259                 // woot, we got an event on a socket :P
260                 eh = ref[intfd];
261                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
262
263                 if (eh == 0)
264                         continue;
265
266                 switch(ov->m_event)
267                 {
268                         case SOCKET_IO_EVENT_WRITE_READY:
269                         {
270                                 eh->m_writeEvent = 0;
271                                 eh->HandleEvent(EVENT_WRITE, 0);
272                         }
273                         break;
274
275                         case SOCKET_IO_EVENT_READ_READY:
276                         {
277                                 if(ov->m_params)
278                                 {
279                                         // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
280                                         udp_overlap * uv = (udp_overlap*)ov->m_params;
281                                         uv->udp_len = len;
282                                         this->udp_ov = uv;
283                                         eh->m_readEvent = 0;
284                                         eh->HandleEvent(EVENT_READ, 0);
285                                         this->udp_ov = 0;
286                                         delete uv;
287                                         PostReadEvent(eh);
288                                 }
289                                 else
290                                 {
291                                         ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
292                                         eh->m_readEvent = 0;
293                                         if(ret != 0 || bytes_recv == 0)
294                                         {
295                                                 /* end of file */
296                                                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
297                                         }
298                                         else
299                                         {
300                                                 eh->HandleEvent(EVENT_READ, 0);
301                                                 PostReadEvent(eh);
302                                         }
303                                 }
304                         }
305                         break;
306                 
307                         case SOCKET_IO_EVENT_ACCEPT:
308                         {
309                                 /* this is kinda messy.. :/ */
310                                 eh->HandleEvent(EVENT_READ, ov->m_params);
311                                 delete ((accept_overlap*)ov->m_params);
312                                 eh->m_acceptEvent = 0;
313                                 PostAcceptEvent(eh);
314                         }
315                         break;
316
317                         case SOCKET_IO_EVENT_ERROR:
318                         {
319                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
320                         }
321                         break;
322                 }
323                 
324                 delete ov;
325         }
326
327         return 0;
328 }
329
330 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
331 {
332         if (!eh)
333                 return;
334
335         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
336         int len = sizeof(sockaddr_in) + 16;
337         DWORD dwBytes;
338         accept_overlap* ao = new accept_overlap;
339         memset(ao->buf, 0, 1024);
340         ao->socket = fd;
341
342         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
343         eh->m_acceptEvent = (void*)ov;
344
345         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
346         {
347                 int err = WSAGetLastError();
348                 if(err != WSA_IO_PENDING)
349                 {
350                         printf("PostAcceptEvent err: %d\n", err);
351                 }
352         }
353 }
354
355
356 std::string IOCPEngine::GetName()
357 {
358         return "iocp";
359 }
360
361 int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
362 {
363         Overlapped* ovl = (Overlapped*)acceptevent;
364         accept_overlap* ov = (accept_overlap*)ovl->m_params;
365
366         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
367         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
368
369         memcpy(addr, client_address, sizeof(sockaddr_in));
370         *addrlen = sizeof(sockaddr_in);
371
372         return ov->socket;
373 }
374
375 int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
376 {
377         Overlapped* ovl = (Overlapped*)acceptevent;
378         accept_overlap* ov = (accept_overlap*)ovl->m_params;
379
380         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
381         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
382
383         memcpy(name, server_address, sizeof(sockaddr_in));
384         *namelen = sizeof(sockaddr_in);
385
386         return 0;
387 }
388
389 int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov)
390 {
391         memcpy(buf, ov->udp_buffer, ov->udp_len);
392         memcpy(from, ov->udp_sockaddr, *fromlen);
393         return ov->udp_len;
394 }
395
396 EventHandler * IOCPEngine::GetRef(int fd)
397 {
398         map<int, EventHandler*>::iterator itr = m_binding.find(fd);
399         return (itr == m_binding.end()) ? 0 : itr->second;
400 }
401
402 bool IOCPEngine::HasFd(int fd)
403 {
404         return (GetRef(fd) != 0);
405 }
406
407 EventHandler * IOCPEngine::GetIntRef(int fd)
408 {
409         if(fd < 0 || fd > MAX_DESCRIPTORS)
410                 return 0;
411         return ref[fd];
412 }
413