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