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