]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengine_iocp.cpp
try...catch around the map::insert in AddFd, for some reason it throws sometimes...
[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         buf.buf = 0;
149         buf.len = 0;
150
151         // determine socket type.
152         DWORD sock_type;
153         int sock_len = sizeof(DWORD);
154         if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
155         {
156                 // wtfhax?
157                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
158                 delete ov;
159                 return;
160         }
161         switch(sock_type)
162         {
163         case SOCK_DGRAM:                        // UDP Socket
164                 {
165                         if(WSARecvFrom(eh->GetFd(), &buf, 1, &r_length, &flags, 0, 0, &ov->m_overlap, 0))
166                         {
167                                 int err = WSAGetLastError();
168                                 if(WSAGetLastError() != WSA_IO_PENDING)
169                                 {
170                                         delete ov;
171                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
172                                         return;
173                                 }
174                         }
175                 }break;
176
177         case SOCK_STREAM:                       // TCP Socket
178                 {
179                         if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
180                         {
181                                 if(WSAGetLastError() != WSA_IO_PENDING)
182                                 {
183                                         delete ov;
184                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
185                                         return;
186                                 }
187                         }
188                 }break;
189
190         default:
191                 {
192                         printf("unknwon socket type: %u\n", sock_type);
193                         return;
194                 }break;
195         }
196         eh->m_readEvent = (void*)ov;
197 }
198
199 int IOCPEngine::DispatchEvents()
200 {
201         DWORD len;
202         LPOVERLAPPED overlap;
203         Overlapped * ov;
204         EventHandler * eh;
205         int intfd;
206         int ret;
207         unsigned long bytes_recv;
208
209         while(GetQueuedCompletionStatus(m_completionPort, &len, (PULONG_PTR)&intfd, &overlap, 100))
210         {
211                 // woot, we got an event on a socket :P
212                 eh = ref[intfd];
213                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
214                 if(eh == 0) continue;
215                 switch(ov->m_event)
216                 {
217                 case SOCKET_IO_EVENT_WRITE_READY:
218                         {
219                                 eh->m_writeEvent = 0;
220                                 eh->HandleEvent(EVENT_WRITE, 0);
221                         }break;
222
223                 case SOCKET_IO_EVENT_READ_READY:
224                         {
225                                 ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
226                                 eh->m_readEvent = 0;
227                                 if(ret != 0 || bytes_recv == 0)
228                                 {
229                                         // end of file
230                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO);
231                                 }
232                                 else
233                                 {
234                                         eh->HandleEvent(EVENT_READ, 0);
235                                         PostReadEvent(eh);
236                                 }
237                         }break;
238                 
239                 case SOCKET_IO_EVENT_ACCEPT:
240                         {
241                                 /* this is kinda messy.. :/ */
242                                 eh->HandleEvent(EVENT_READ, ov->m_params);
243                                 delete ((accept_overlap*)ov->m_params);
244                                 eh->m_acceptEvent = 0;
245                                 PostAcceptEvent(eh);
246                         }break;
247
248                 case SOCKET_IO_EVENT_ERROR:
249                         {
250                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
251                         }break;
252                 }
253                 
254                 delete ov;
255         }
256
257         return 0;
258 }
259
260 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
261 {
262         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
263         int len = sizeof(sockaddr_in) + 16;
264         DWORD dwBytes;
265         accept_overlap * ao = new accept_overlap;
266         memset(ao->buf, 0, 1024);
267         ao->socket = fd;
268
269         Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
270         eh->m_acceptEvent = (void*)ov;
271
272         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
273         {
274                 int err = WSAGetLastError();
275                 if(err != WSA_IO_PENDING)
276                 {
277                         printf("PostAcceptEvent err: %d\n", err);
278                 }
279         }
280 }
281
282
283 std::string IOCPEngine::GetName()
284 {
285         return "iocp";
286 }
287
288 int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
289 {
290         Overlapped * ovl = (Overlapped*)acceptevent;
291         accept_overlap * ov = (accept_overlap*)ovl->m_params;
292
293         sockaddr_in * server_address = (sockaddr_in*)&ov->buf[10];
294         sockaddr_in * client_address = (sockaddr_in*)&ov->buf[38];
295
296         memcpy(addr, client_address, sizeof(sockaddr_in));
297         *addrlen = sizeof(sockaddr_in);
298
299         return ov->socket;
300 }
301
302 int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
303 {
304         Overlapped * ovl = (Overlapped*)acceptevent;
305         accept_overlap * ov = (accept_overlap*)ovl->m_params;
306
307         sockaddr_in * server_address = (sockaddr_in*)&ov->buf[10];
308         sockaddr_in * client_address = (sockaddr_in*)&ov->buf[38];
309
310         memcpy(name, server_address, sizeof(sockaddr_in));
311         *namelen = sizeof(sockaddr_in);
312
313         return 0;
314 }
315
316 EventHandler * IOCPEngine::GetRef(int fd)
317 {
318         map<int, EventHandler*>::iterator itr = m_binding.find(fd);
319         return (itr == m_binding.end()) ? 0 : itr->second;
320 }
321
322 bool IOCPEngine::HasFd(int fd)
323 {
324         return (GetRef(fd) != 0);
325 }
326
327 EventHandler * IOCPEngine::GetIntRef(int fd)
328 {
329         if(fd < 0 || fd > MAX_DESCRIPTORS)
330                 return 0;
331         return ref[fd];
332 }