From a4b3dc92003178088fa31bc6df9dfbe1cd1c18d1 Mon Sep 17 00:00:00 2001 From: danieldg Date: Sat, 24 Oct 2009 20:04:24 +0000 Subject: Remove IOCP socket engine IOCP has been experimental since its introduction, and has not worked correctly for some time. Since we have no windows developer, remove the dead code. Most of the advantages of IOCP are not used by an ircd process regardless: we do not use threads for our client processing, and we add an extra copy operation that eliminates any gain from OS-level buffer reuse. git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@11970 e03df62e-2008-0410-955e-edbf42e46eb7 --- src/socketengines/socketengine_iocp.cpp | 753 ------------------------------ src/socketengines/socketengine_select.cpp | 3 +- 2 files changed, 1 insertion(+), 755 deletions(-) delete mode 100644 src/socketengines/socketengine_iocp.cpp (limited to 'src/socketengines') diff --git a/src/socketengines/socketengine_iocp.cpp b/src/socketengines/socketengine_iocp.cpp deleted file mode 100644 index 17c439c5b..000000000 --- a/src/socketengines/socketengine_iocp.cpp +++ /dev/null @@ -1,753 +0,0 @@ -/* +------------------------------------+ - * | 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. - * - * --------------------------------------------------- - */ - -#include "inspircd_config.h" -#ifdef CONFIG_USE_IOCP - -#ifndef __SOCKETENGINE_IOCP__ -#define __SOCKETENGINE_IOCP__ - -#include "inspircd_win32wrapper.h" -#include "inspircd.h" -#include "socketengine.h" - -#define READ_BUFFER_SIZE 600 -#define USING_IOCP 1 - -/** 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 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 - -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->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; - MAX_DESCRIPTORS = 10240; - ref = new EventHandler* [10240]; - memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS); -} - -IOCPEngine::~IOCPEngine() -{ - /* Clean up winsock and close completion port */ - CloseHandle(m_completionPort); - WSACleanup(); - delete[] ref; -} - -bool IOCPEngine::AddFd(EventHandler* eh, int event_mask) -{ - /* Does it at least look valid? */ - if (!eh) - return false; - - int* fake_fd = new int(GenerateFd(eh->GetFd())); - int is_accept = 0; - int opt_len = sizeof(int); - - /* In range? */ - if ((*fake_fd < 0) || (*fake_fd > MAX_DESCRIPTORS)) - { - delete fake_fd; - return false; - } - - /* Already an entry here */ - if (ref[*fake_fd]) - { - delete fake_fd; - return false; - } - - /* are we a listen socket? */ - 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 */ - fdExt.set(eh, *fake_fd); - - unsigned long completion_key = (ULONG_PTR)*fake_fd; - /* assign the socket to the completion port */ - if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0)) - return false; - - /* setup initial events */ - if(is_accept) - PostAcceptEvent(eh); - else - PostReadEvent(eh); - - /* log message */ - 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 (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 - { - m_binding.insert( std::map::value_type( eh->GetFd(), eh ) ); - } - catch (...) - { - /* Ohshi-, map::insert failed :/ */ - return false; - } - - ++CurrentSetSize; - SocketEngine::SetEventMask(eh, event_mask); - ref[*fake_fd] = eh; - - return true; -} - -bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */) -{ - if (!eh) - return false; - - int* fake_fd = (int*)fdExt.get(eh); - - if (!fake_fd) - return false; - - int fd = eh->GetFd(); - - void* m_readEvent = (void*)readExt.get(eh); - void* m_writeEvent = (void*)writeExt.get(eh); - void* m_acceptEvent = (void*)acceptExt.get(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 (m_readEvent) - { - if(((Overlapped*)m_readEvent)->m_params != 0) - delete ((udp_overlap*)((Overlapped*)m_readEvent)->m_params); - - delete ((Overlapped*)m_readEvent); - readExt.free(eh); - } - - if(m_writeEvent) - { - delete ((Overlapped*)m_writeEvent); - writeExt.free(eh); - } - - if(m_acceptEvent) - { - delete ((accept_overlap*)((Overlapped*)m_acceptEvent)->m_params); - delete ((Overlapped*)m_acceptEvent); - acceptExt.free(eh); - } - - /* Clear binding */ - ref[*fake_fd] = 0; - m_binding.erase(eh->GetFd()); - - delete fake_fd; - fdExt.free(eh); - - /* decrement set size */ - --CurrentSetSize; - - /* success */ - return true; -} - -void IOCPEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) -{ - if (!eh) - return; - - void* m_writeEvent = (void*)writeExt.get(eh); - - int* fake_fd = (int*)fdExt.get(eh); - if (!fake_fd) - return; - - /* Post event - write begin */ - 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); - writeExt.free(eh); - writeExt.set(eh, (intptr_t)ov); - PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); - } -} - -bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param) -{ - if (!eh) - return false; - - int* fake_fd = (int*)fdExt.get(eh); - if (!fake_fd) - return false; - - Overlapped * ov = new Overlapped(type, param); - ULONG_PTR completion_key = (ULONG_PTR)*fake_fd; - return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); -} - -void IOCPEngine::PostReadEvent(EventHandler * eh) -{ - if (!eh) - return; - - Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0); - DWORD flags = 0; - DWORD r_length = 0; - WSABUF buf; - - /* by passing a null buffer pointer, we can have this working in the same way as epoll.. - * its slower, but it saves modifying all network code. - */ - buf.buf = 0; - buf.len = 0; - - /* determine socket type. */ - DWORD sock_type; - int sock_len = sizeof(DWORD); - if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1) - { - /* wtfhax? */ - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); - delete ov; - return; - } - switch(sock_type) - { - case SOCK_DGRAM: /* UDP Socket */ - { - udp_overlap * uv = new udp_overlap; - uv->udp_sockaddr_len = sizeof(sockaddr); - buf.buf = (char*)uv->udp_buffer; - buf.len = sizeof(uv->udp_buffer); - ov->m_params = (unsigned long)uv; - if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0)) - { - int err = WSAGetLastError(); - if(err != WSA_IO_PENDING) - { - delete ov; - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); - return; - } - } - } - break; - - case SOCK_STREAM: /* TCP Socket */ - { - if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR) - { - if(WSAGetLastError() != WSA_IO_PENDING) - { - delete ov; - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); - return; - } - } - } - break; - - default: - { - printf("unknwon socket type: %u\n", sock_type); - return; - } - break; - } - readExt.set(eh, (intptr_t)ov); -} - -int IOCPEngine::DispatchEvents() -{ - DWORD len; - LPOVERLAPPED overlap; - Overlapped * ov; - EventHandler * eh; - ULONG_PTR intfd; - int ret; - unsigned long bytes_recv; - - while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000)) - { - if (intfd > (unsigned long)MAX_DESCRIPTORS) - continue; - - // woot, we got an event on a socket :P - eh = ref[intfd]; - ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap); - - if (eh == 0) - continue; - - TotalEvents++; - - switch(ov->m_event) - { - case SOCKET_IO_EVENT_WRITE_READY: - { - WriteEvents++; - 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; - readExt.free(eh); - eh->HandleEvent(EVENT_READ, 0); - this->udp_ov = 0; - delete uv; - PostReadEvent(eh); - } - else - { - ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv); - readExt.free(eh); - if(ret != 0 || bytes_recv == 0) - { - /* end of file */ - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */ - } - else - { - eh->HandleEvent(EVENT_READ, 0); - PostReadEvent(eh); - } - } - } - break; - - case SOCKET_IO_EVENT_ACCEPT: - { - /* this is kinda messy.. :/ */ - ReadEvents++; - eh->HandleEvent(EVENT_READ, ov->m_params); - delete ((accept_overlap*)ov->m_params); - acceptExt.free(eh); - PostAcceptEvent(eh); - } - break; - - case SOCKET_IO_EVENT_ERROR: - { - ErrorEvents++; - eh->HandleEvent(EVENT_ERROR, ov->m_params); - } - break; - } - - delete ov; - } - - return 0; -} - -void IOCPEngine::PostAcceptEvent(EventHandler * eh) -{ - if (!eh) - return; - - int on = 1; - u_long arg = 1; - struct linger linger = { 0 }; - - int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED); - - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); - /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */ - linger.l_onoff = 1; - linger.l_linger = 1; - setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger)); - ioctlsocket(fd, FIONBIO, &arg); - - int len = sizeof(sockaddr_in) + 16; - DWORD dwBytes; - accept_overlap* ao = new accept_overlap; - memset(ao->buf, 0, 1024); - ao->socket = fd; - - Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao); - acceptExt.set(eh, (intptr_t)ov); - - if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE) - { - int err = WSAGetLastError(); - if(err != WSA_IO_PENDING) - { - printf("PostAcceptEvent err: %d\n", err); - } - } -} - - -std::string IOCPEngine::GetName() -{ - return "iocp"; -} - -EventHandler * IOCPEngine::GetRef(int fd) -{ - std::map::iterator itr = m_binding.find(fd); - return (itr == m_binding.end()) ? 0 : itr->second; -} - -bool IOCPEngine::HasFd(int fd) -{ - return (GetRef(fd) != 0); -} - -bool IOCPEngine::BoundsCheckFd(EventHandler* eh) -{ - int* internal_fd = (int*)fdExt.get(eh); - if (!eh || eh->GetFd() < 0) - return false; - - if(!internal_fd) - return false; - - if(*internal_fd > MAX_DESCRIPTORS) - return false; - - return true; -} - -EventHandler * IOCPEngine::GetIntRef(int fd) -{ - if(fd < 0 || fd > MAX_DESCRIPTORS) - return 0; - return ref[fd]; -} - -int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen) -{ - //SOCKET s = fd->GetFd(); - - 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* client_address = (sockaddr_in*)&ov->buf[38]; - - memcpy(addr, client_address, sizeof(sockaddr_in)); - *addrlen = sizeof(sockaddr_in); - - return ov->socket; -} - -int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen) -{ - 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]; - - memcpy(name, server_address, sizeof(sockaddr_in)); - *namelen = sizeof(sockaddr_in); - - return 0; -} - -int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) -{ - 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 ov->udp_len; -} - -int IOCPEngine::Blocking(int fd) -{ - unsigned long opt = 0; - return ioctlsocket(fd, FIONBIO, &opt); -} - -int IOCPEngine::NonBlocking(int fd) -{ - unsigned long opt = 1; - return ioctlsocket(fd, FIONBIO, &opt); -} - -int IOCPEngine::Close(int fd) -{ - return closesocket(fd); -} - -int IOCPEngine::Close(EventHandler* fd) -{ - return this->Close(fd->GetFd()); -} - -SocketEngine* CreateSocketEngine() -{ - return new IOCPEngine; -} -#endif \ No newline at end of file diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp index 6965ee771..7767a86c3 100644 --- a/src/socketengines/socketengine_select.cpp +++ b/src/socketengines/socketengine_select.cpp @@ -12,7 +12,6 @@ */ #include "inspircd_config.h" -#ifndef CONFIG_USE_IOCP #include "inspircd.h" #include "socketengine.h" @@ -101,7 +100,7 @@ int SelectEngine::DispatchEvents() FD_ZERO(&rfdset); FD_ZERO(&errfdset); - /* Populate the select FD sets (this is why select sucks compared to epoll, kqueue, IOCP) */ + /* Populate the select FD sets (this is why select sucks compared to epoll, kqueue) */ for (unsigned int i = 0; i < FD_SETSIZE; i++) { EventHandler* eh = ref[i]; -- cgit v1.2.3