]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengine_iocp.cpp
Fix problem where two unrelated options are named identically, thanks for reminding...
[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                 delete ((Overlapped*)eh->m_readEvent);
102
103         if(eh->m_writeEvent != 0)
104                 delete ((Overlapped*)eh->m_writeEvent);
105
106         if(eh->m_acceptEvent != 0)
107         {
108                 delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params);
109                 delete ((Overlapped*)eh->m_acceptEvent);
110         }
111
112         /* Clear binding */
113         ref[fake_fd] = 0;
114         m_binding.erase(eh->GetFd());
115
116         /* decrement set size */
117         --CurrentSetSize;
118         
119         /* success */
120         return true;
121 }
122
123 void IOCPEngine::WantWrite(EventHandler* eh)
124 {
125         /* Post event - write begin */
126         if(!eh->m_writeEvent)
127         {
128                 ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
129                 Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
130                 eh->m_writeEvent = (void*)ov;
131                 PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
132         }
133 }
134
135 bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
136 {
137         Overlapped * ov = new Overlapped(type, param);
138         ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
139         return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
140 }
141
142 void IOCPEngine::PostReadEvent(EventHandler * eh)
143 {
144         Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
145         DWORD flags = 0;
146         DWORD r_length = 0;
147         WSABUF buf;
148
149         /* by passing a null buffer pointer, we can have this working in the same way as epoll..
150          * its slower, but it saves modifying all network code.
151          */
152         buf.buf = 0;
153         buf.len = 0;
154
155         /* determine socket type. */
156         DWORD sock_type;
157         int sock_len = sizeof(DWORD);
158         if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
159         {
160                 /* wtfhax? */
161                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
162                 delete ov;
163                 return;
164         }
165         switch(sock_type)
166         {
167                 case SOCK_DGRAM:                        /* UDP Socket */
168                 {
169                         if(WSARecvFrom(eh->GetFd(), &buf, 1, &r_length, &flags, 0, 0, &ov->m_overlap, 0))
170                         {
171                                 int err = WSAGetLastError();
172                                 if(WSAGetLastError() != WSA_IO_PENDING)
173                                 {
174                                         delete ov;
175                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
176                                         return;
177                                 }
178                         }
179                 }
180                 break;
181
182                 case SOCK_STREAM:                       /* TCP Socket */
183                 {
184                         if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
185                         {
186                                 if(WSAGetLastError() != WSA_IO_PENDING)
187                                 {
188                                         delete ov;
189                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
190                                         return;
191                                 }
192                         }
193                 }
194                 break;
195
196                 default:
197                 {
198                         printf("unknwon socket type: %u\n", sock_type);
199                         return;
200                 }
201                 break;
202         }
203         eh->m_readEvent = (void*)ov;
204 }
205
206 int IOCPEngine::DispatchEvents()
207 {
208         DWORD len;
209         LPOVERLAPPED overlap;
210         Overlapped * ov;
211         EventHandler * eh;
212         ULONG_PTR intfd;
213         int ret;
214         unsigned long bytes_recv;
215
216         while(GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
217         {
218                 // woot, we got an event on a socket :P
219                 eh = ref[intfd];
220                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
221                 if(eh == 0) continue;
222                 switch(ov->m_event)
223                 {
224                         case SOCKET_IO_EVENT_WRITE_READY:
225                         {
226                                 eh->m_writeEvent = 0;
227                                 eh->HandleEvent(EVENT_WRITE, 0);
228                         }
229                         break;
230
231                         case SOCKET_IO_EVENT_READ_READY:
232                         {
233                                 ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
234                                 eh->m_readEvent = 0;
235                                 if(ret != 0 || bytes_recv == 0)
236                                 {
237                                         /* end of file */
238                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
239                                 }
240                                 else
241                                 {
242                                         eh->HandleEvent(EVENT_READ, 0);
243                                         PostReadEvent(eh);
244                                 }
245                         }
246                         break;
247                 
248                         case SOCKET_IO_EVENT_ACCEPT:
249                         {
250                                 /* this is kinda messy.. :/ */
251                                 eh->HandleEvent(EVENT_READ, ov->m_params);
252                                 delete ((accept_overlap*)ov->m_params);
253                                 eh->m_acceptEvent = 0;
254                                 PostAcceptEvent(eh);
255                         }
256                         break;
257
258                         case SOCKET_IO_EVENT_ERROR:
259                         {
260                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
261                         }
262                         break;
263                 }
264                 
265                 delete ov;
266         }
267
268         return 0;
269 }
270
271 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
272 {
273         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
274         int len = sizeof(sockaddr_in) + 16;
275         DWORD dwBytes;
276         accept_overlap* ao = new accept_overlap;
277         memset(ao->buf, 0, 1024);
278         ao->socket = fd;
279
280         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
281         eh->m_acceptEvent = (void*)ov;
282
283         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
284         {
285                 int err = WSAGetLastError();
286                 if(err != WSA_IO_PENDING)
287                 {
288                         printf("PostAcceptEvent err: %d\n", err);
289                 }
290         }
291 }
292
293
294 std::string IOCPEngine::GetName()
295 {
296         return "iocp";
297 }
298
299 int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
300 {
301         Overlapped* ovl = (Overlapped*)acceptevent;
302         accept_overlap* ov = (accept_overlap*)ovl->m_params;
303
304         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
305         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
306
307         memcpy(addr, client_address, sizeof(sockaddr_in));
308         *addrlen = sizeof(sockaddr_in);
309
310         return ov->socket;
311 }
312
313 int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
314 {
315         Overlapped* ovl = (Overlapped*)acceptevent;
316         accept_overlap* ov = (accept_overlap*)ovl->m_params;
317
318         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
319         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
320
321         memcpy(name, server_address, sizeof(sockaddr_in));
322         *namelen = sizeof(sockaddr_in);
323
324         return 0;
325 }
326
327 EventHandler * IOCPEngine::GetRef(int fd)
328 {
329         map<int, EventHandler*>::iterator itr = m_binding.find(fd);
330         return (itr == m_binding.end()) ? 0 : itr->second;
331 }
332
333 bool IOCPEngine::HasFd(int fd)
334 {
335         return (GetRef(fd) != 0);
336 }
337
338 EventHandler * IOCPEngine::GetIntRef(int fd)
339 {
340         if(fd < 0 || fd > MAX_DESCRIPTORS)
341                 return 0;
342         return ref[fd];
343 }
344