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