* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
- * InspIRCd: (C) 2002-2008 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
+ * InspIRCd: (C) 2002-2009 InspIRCd Development Team
+ * See: http://wiki.inspircd.org/Credits
*
* This program is free but copyrighted software; see
* the file COPYING for details.
* ---------------------------------------------------
*/
-#include "socketengines/socketengine_iocp.h"
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2009 InspIRCd Development Team
+ * See: http://wiki.inspircd.org/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef __SOCKETENGINE_IOCP__
+#define __SOCKETENGINE_IOCP__
+
+#define READ_BUFFER_SIZE 600
+#define USING_IOCP 1
+
+#include "inspircd_config.h"
+#include "inspircd_win32wrapper.h"
+#include "inspircd.h"
+#include "socketengine.h"
+
+/** Socket overlapped event types
+ */
+enum SocketIOEvent
+{
+ /** Read ready */
+ SOCKET_IO_EVENT_READ_READY = 0,
+ /** Write ready */
+ SOCKET_IO_EVENT_WRITE_READY = 1,
+ /** Accept ready */
+ SOCKET_IO_EVENT_ACCEPT = 2,
+ /** Error occured */
+ SOCKET_IO_EVENT_ERROR = 3,
+ /** Number of events */
+ NUM_SOCKET_IO_EVENTS = 4,
+};
+
+/** Represents a windows overlapped IO event
+ */
+class Overlapped
+{
+ public:
+ /** Overlap event */
+ OVERLAPPED m_overlap;
+ /** Type of event */
+ SocketIOEvent m_event;
+#ifdef WIN64
+ /** Parameters */
+ unsigned __int64 m_params;
+#else
+ /** Parameters */
+ unsigned long m_params;
+#endif
+ /** Create an overlapped event
+ */
+ Overlapped(SocketIOEvent ev, int params) : m_event(ev), m_params(params)
+ {
+ memset(&m_overlap, 0, sizeof(OVERLAPPED));
+ }
+};
+
+/** Specific to UDP sockets with overlapped IO
+ */
+struct udp_overlap
+{
+ unsigned char udp_buffer[600];
+ unsigned long udp_len;
+ sockaddr udp_sockaddr[2];
+ unsigned long udp_sockaddr_len;
+};
+
+/** Specific to accepting sockets with overlapped IO
+ */
+struct accept_overlap
+{
+ int socket;
+ char buf[1024];
+};
+
+/** Implementation of SocketEngine that implements windows IO Completion Ports
+ */
+class IOCPEngine : public SocketEngine
+{
+ /** Creates a "fake" file descriptor for use with an IOCP socket.
+ * This is a little slow, but it isnt called too much. We'll fix it
+ * in a future release.
+ * @return -1 if there are no free slots, and an integer if it finds one.
+ */
+ __inline int GenerateFd(int RealFd)
+ {
+ int index_hash = RealFd % MAX_DESCRIPTORS;
+ if(ref[index_hash] == 0)
+ return index_hash;
+ else
+ {
+ register int i = 0;
+ for(; i < MAX_DESCRIPTORS; ++i)
+ if(ref[i] == 0)
+ return i;
+ }
+ return -1;
+ }
+
+ /** Global I/O completion port that sockets attach to.
+ */
+ HANDLE m_completionPort;
+
+ /** This is kinda shitty... :/ for getting an address from a real fd.
+ */
+ std::map<int, EventHandler*> m_binding;
+
+ LocalIntExt fdExt;
+ LocalIntExt readExt;
+ LocalIntExt writeExt;
+ LocalIntExt acceptExt;
+
+public:
+ /** Holds the preallocated buffer passed to WSARecvFrom
+ * function. Yes, I know, it's a dirty hack.
+ */
+ udp_overlap * udp_ov;
+
+ /** Creates an IOCP Socket Engine
+ * @param Instance The creator of this object
+ */
+ IOCPEngine();
+
+ /** Deletes an IOCP socket engine and all the attached sockets
+ */
+ ~IOCPEngine();
+
+ /** Adds an event handler to the completion port, and sets up initial events.
+ * @param eh EventHandler to add
+ * @return True if success, false if no room
+ */
+ bool AddFd(EventHandler* eh, int event_mask);
+
+ /** Gets the maximum number of file descriptors that this engine can handle.
+ * @return The number of file descriptors
+ */
+ __inline int GetMaxFds() { return MAX_DESCRIPTORS; }
+
+ /** Gets the number of free/remaining file descriptors under this engine.
+ * @return Remaining count
+ */
+ __inline int GetRemainingFds()
+ {
+ register int count = 0;
+ register int i = 0;
+ for(; i < MAX_DESCRIPTORS; ++i)
+ if(ref[i] == 0)
+ ++count;
+ return count;
+ }
+
+ /** Removes a file descriptor from the set, preventing it from receiving any more events
+ * @return True if remove was successful, false otherwise
+ */
+ bool DelFd(EventHandler* eh, bool force = false);
+
+ /** Called every loop to handle input/output events for all sockets under this engine
+ * @return The number of "changed" sockets.
+ */
+ int DispatchEvents();
+
+ /** Gets the name of this socket engine as a string.
+ * @return string of socket engine name
+ */
+ std::string GetName();
+
+ void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
+
+ /** Posts a completion event on the specified socket.
+ * @param eh EventHandler for message
+ * @param type Event Type
+ * @param param Event Parameter
+ * @return True if added, false if not
+ */
+ bool PostCompletionEvent(EventHandler* eh, SocketIOEvent type, int param);
+
+ /** Posts a read event on the specified socket
+ * @param eh EventHandler (socket)
+ */
+ void PostReadEvent(EventHandler* eh);
+
+ /** Posts an accept event on the specified socket
+ * @param eh EventHandler (socket)
+ */
+ void PostAcceptEvent(EventHandler* eh);
+
+ /** Returns the EventHandler attached to a specific fd.
+ * If the fd isnt in the socketengine, returns NULL.
+ * @param fd The event handler to look for
+ * @return A pointer to the event handler, or NULL
+ */
+ EventHandler* GetRef(int fd);
+
+ /** Returns true if a file descriptor exists in
+ * the socket engine's list.
+ * @param fd The event handler to look for
+ * @return True if this fd has an event handler
+ */
+ bool HasFd(int fd);
+
+ /** Returns the EventHandler attached to a specific fd.
+ * If the fd isnt in the socketengine, returns NULL.
+ * @param fd The event handler to look for
+ * @return A pointer to the event handler, or NULL
+ */
+ EventHandler* GetIntRef(int fd);
+
+ bool BoundsCheckFd(EventHandler* eh);
+
+ virtual int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen);
+
+ virtual int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
+
+ virtual int Blocking(int fd);
+
+ virtual int NonBlocking(int fd);
+
+ virtual int GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen);
+
+ virtual int Close(int fd);
+
+ virtual int Close(EventHandler* fd);
+};
+
+#endif
+
#include "exitcodes.h"
#include <mswsock.h>
-IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
+IOCPEngine::IOCPEngine()
+: fdExt("internal_fd", NULL),
+ readExt("windows_readevent", NULL),
+ writeExt("windows_writeevent", NULL),
+ acceptExt("windows_acceptevent", NULL)
{
+ MAX_DESCRIPTORS = 10240;
+
/* Create completion port */
m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
if (!m_completionPort)
{
- ServerInstance->Log(DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
- ServerInstance->Log(DEFAULT, "ERROR: this is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
+ ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.\n");
printf("ERROR: this is a fatal error, exiting now.\n");
ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
/* Null variables out. */
CurrentSetSize = 0;
- EngineHandle = 0;
+ MAX_DESCRIPTORS = 10240;
+ ref = new EventHandler* [10240];
memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
}
/* Clean up winsock and close completion port */
CloseHandle(m_completionPort);
WSACleanup();
+ delete[] ref;
}
-bool IOCPEngine::AddFd(EventHandler* eh)
+bool IOCPEngine::AddFd(EventHandler* eh, int event_mask)
{
/* Does it at least look valid? */
if (!eh)
getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
/* set up the read event so the socket can actually receive data :P */
- eh->Extend("internal_fd", fake_fd);
+ fdExt.set(eh, *fake_fd);
unsigned long completion_key = (ULONG_PTR)*fake_fd;
/* assign the socket to the completion port */
PostReadEvent(eh);
/* log message */
- ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
+ ServerInstance->Logs->Log("SOCKET",DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
/* post a write event if there is data to be written */
- if(eh->Writeable())
- WantWrite(eh);
+ if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
+ OnSetEvent(eh, event_mask, event_mask);
/* we're all good =) */
try
}
++CurrentSetSize;
+ SocketEngine::SetEventMask(eh, event_mask);
ref[*fake_fd] = eh;
return true;
if (!eh)
return false;
- int* fake_fd = NULL;
+ int* fake_fd = (int*)fdExt.get(eh);
- if (!eh->GetExt("internal_fd", fake_fd))
+ if (!fake_fd)
return false;
int fd = eh->GetFd();
- void* m_readEvent = NULL;
- void* m_writeEvent = NULL;
- void* m_acceptEvent = NULL;
+ void* m_readEvent = (void*)readExt.get(eh);
+ void* m_writeEvent = (void*)writeExt.get(eh);
+ void* m_acceptEvent = (void*)acceptExt.get(eh);
- ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
+ ServerInstance->Logs->Log("SOCKET",DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
/* Cancel pending i/o operations. */
if (CancelIo((HANDLE)fd) == FALSE)
return false;
/* Free the buffer, and delete the event. */
- if (eh->GetExt("windows_readevent", m_readEvent))
+ if (m_readEvent)
{
if(((Overlapped*)m_readEvent)->m_params != 0)
delete ((udp_overlap*)((Overlapped*)m_readEvent)->m_params);
delete ((Overlapped*)m_readEvent);
- eh->Shrink("windows_readevent");
+ readExt.free(eh);
}
- if(eh->GetExt("windows_writeevent", m_writeEvent))
+ if(m_writeEvent)
{
delete ((Overlapped*)m_writeEvent);
- eh->Shrink("windows_writeevent");
+ writeExt.free(eh);
}
- if(eh->GetExt("windows_acceptevent", m_acceptEvent))
+ if(m_acceptEvent)
{
delete ((accept_overlap*)((Overlapped*)m_acceptEvent)->m_params);
delete ((Overlapped*)m_acceptEvent);
- eh->Shrink("windows_accepevent");
+ acceptExt.free(eh);
}
/* Clear binding */
m_binding.erase(eh->GetFd());
delete fake_fd;
- eh->Shrink("internal_fd");
+ fdExt.free(eh);
/* decrement set size */
--CurrentSetSize;
-
+
/* success */
return true;
}
-void IOCPEngine::WantWrite(EventHandler* eh)
+void IOCPEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
if (!eh)
return;
-
- void* m_writeEvent = NULL;
- int* fake_fd = NULL;
- if (!eh->GetExt("internal_fd", fake_fd))
+ void* m_writeEvent = (void*)writeExt.get(eh);
+
+ int* fake_fd = (int*)fdExt.get(eh);
+ if (!fake_fd)
return;
/* Post event - write begin */
- if(!eh->GetExt("windows_writeevent", m_writeEvent))
+ if((new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !m_writeEvent)
{
ULONG_PTR completion_key = (ULONG_PTR)*fake_fd;
Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
- eh->Shrink("windows_writeevent");
- eh->Extend("windows_writeevent",ov);
+ writeExt.free(eh);
+ writeExt.set(eh, (intptr_t)ov);
PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
}
}
if (!eh)
return false;
- int* fake_fd = NULL;
- if (!eh->GetExt("internal_fd", fake_fd))
+ int* fake_fd = (int*)fdExt.get(eh);
+ if (!fake_fd)
return false;
Overlapped * ov = new Overlapped(type, param);
}
break;
}
- eh->Extend("windows_readevent", ov);
+ readExt.set(eh, (intptr_t)ov);
}
int IOCPEngine::DispatchEvents()
while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
{
- if (intfd < 0 || intfd > MAX_DESCRIPTORS)
+ if (intfd > (unsigned long)MAX_DESCRIPTORS)
continue;
// woot, we got an event on a socket :P
if (eh == 0)
continue;
- void* m_readEvent = NULL;
- void* m_writeEvent = NULL;
-
- eh->GetExt("windows_readevent", m_readEvent);
- eh->GetExt("windows_writeevent", m_writeEvent);
-
TotalEvents++;
switch(ov->m_event)
case SOCKET_IO_EVENT_WRITE_READY:
{
WriteEvents++;
- eh->Shrink("windows_writeevent");
+ writeExt.free(eh);
+ SetEventMask(eh, eh->GetEventMask() & ~FD_WRITE_WILL_BLOCK);
eh->HandleEvent(EVENT_WRITE, 0);
}
break;
case SOCKET_IO_EVENT_READ_READY:
{
ReadEvents++;
+ SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
if(ov->m_params)
{
// if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
udp_overlap * uv = (udp_overlap*)ov->m_params;
uv->udp_len = len;
this->udp_ov = uv;
- eh->Shrink("windows_readevent");
+ readExt.free(eh);
eh->HandleEvent(EVENT_READ, 0);
this->udp_ov = 0;
delete uv;
else
{
ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
- eh->Shrink("windows_readevent");
+ readExt.free(eh);
if(ret != 0 || bytes_recv == 0)
{
/* end of file */
}
}
break;
-
+
case SOCKET_IO_EVENT_ACCEPT:
{
/* this is kinda messy.. :/ */
ReadEvents++;
eh->HandleEvent(EVENT_READ, ov->m_params);
delete ((accept_overlap*)ov->m_params);
- eh->Shrink("windows_acceptevent");
+ acceptExt.free(eh);
PostAcceptEvent(eh);
}
break;
}
break;
}
-
+
delete ov;
}
ao->socket = fd;
Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
- eh->Extend("windows_acceptevent", ov);
+ acceptExt.set(eh, (intptr_t)ov);
if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
{
bool IOCPEngine::BoundsCheckFd(EventHandler* eh)
{
- int * internal_fd;
+ int* internal_fd = (int*)fdExt.get(eh);
if (!eh || eh->GetFd() < 0)
return false;
- if(!eh->GetExt("internal_fd", internal_fd))
+ if(!internal_fd)
return false;
if(*internal_fd > MAX_DESCRIPTORS)
int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen)
{
- SOCKET s = fd->GetFd();
+ //SOCKET s = fd->GetFd();
- Overlapped* acceptevent = NULL;
- if (!fd->GetExt("windows_acceptevent", acceptevent))
+ Overlapped* acceptevent = (Overlapped*)acceptExt.get(fd);
+ if (!acceptevent)
/* Shit, no accept event on this socket! :( */
return -1;
Overlapped* ovl = acceptevent;
accept_overlap* ov = (accept_overlap*)ovl->m_params;
-
- sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
+
+ //sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
memcpy(addr, client_address, sizeof(sockaddr_in));
int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen)
{
- Overlapped* ovl = NULL;
-
- if (!fd->GetExt("windows_acceptevent", ovl))
+ Overlapped* ovl = (Overlapped*)acceptExt.get(fd);
+
+ if (!ovl)
return -1;
accept_overlap* ov = (accept_overlap*)ovl->m_params;
sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
- sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
+ //sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
memcpy(name, server_address, sizeof(sockaddr_in));
*namelen = sizeof(sockaddr_in);
int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
{
- udp_overlap * ov = NULL;
- if (!fd->GetExt("windows_readevent", ov))
+ this->UpdateStats(len, 0);
+ udp_overlap* ov = (udp_overlap*)readExt.get(fd);
+ if (!ov)
return -1;
memcpy(buf, ov->udp_buffer, ov->udp_len);
memcpy(from, ov->udp_sockaddr, *fromlen);
return this->Close(fd->GetFd());
}
+SocketEngine* CreateSocketEngine()
+{
+ return new IOCPEngine;
+}