From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/libs/network/netmanager/netmanager_win.c | 464 +++++++++++++++++++++++++++ 1 file changed, 464 insertions(+) create mode 100644 src/libs/network/netmanager/netmanager_win.c (limited to 'src/libs/network/netmanager/netmanager_win.c') diff --git a/src/libs/network/netmanager/netmanager_win.c b/src/libs/network/netmanager/netmanager_win.c new file mode 100644 index 0000000..f146732 --- /dev/null +++ b/src/libs/network/netmanager/netmanager_win.c @@ -0,0 +1,464 @@ +/* + * Copyright 2006 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define PORT_WANT_ERRNO +#include "port.h" +#include "../netport.h" + +#define NETMANAGER_INTERNAL +#define NETDESCRIPTOR_INTERNAL +#define SOCKET_INTERNAL +#include "netmanager_win.h" +#include "../socket/socket.h" + +#include "ndesc.h" +#include "types.h" +#include "libs/misc.h" +#include "libs/log.h" + +#include +#include + +#include "netmanager_common.ci" + +int closeWSAEvent(WSAEVENT event); + + +// The elements of the following arrays with the same index belong to +// eachother. +#define MAX_SOCKETS WSA_MAXIMUM_WAIT_EVENTS + // We cannot have more sockets than we can have events, as + // all may need an event at some point. +static NetDescriptor *netDescriptors[MAX_SOCKETS]; +static WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS]; +static size_t numSockets; +static size_t numActiveEvents; +// For each NetDescriptor registered with the NetManager, an event +// is created. Only the first numActiveEvents will be processed though. +// Inv: numActiveEvents <= numSockets +// Inv: for all i: 0 <= i < numActiveEvents: events[i] is active +// (events[i] being active also means netDescriptor[i]->smd->eventMask +// != 0) +// Inv: for all i: 0 <= i < numSockets: netDescriptor[i]->smd->index == i + +void +NetManager_init(void) { + numActiveEvents = 0; +} + +void +NetManager_uninit(void) { + assert(numActiveEvents == 0); +} + +static inline SocketManagementDataWin * +SocketManagementData_alloc(void) { + return malloc(sizeof (SocketManagementDataWin)); +} + +static inline void +SocketManagementData_free(SocketManagementDataWin *smd) { + free(smd); +} + +// XXX: This function should be moved to some file with generic network +// functions. +int +closeWSAEvent(WSAEVENT event) { + for (;;) { + int error; + + if (WSACloseEvent(event)) + break; + + error = WSAGetLastError(); + if (error != WSAEINPROGRESS) { + log_add(log_Error, + "WSACloseEvent() failed with error code %d.", error); + errno = winsockErrorToErrno(error); + return -1; + } + } + return 0; +} + +// Register the NetDescriptor with the NetManager. +int +NetManager_addDesc(NetDescriptor *nd) { + long eventMask = 0; + WSAEVENT event; + + if (numSockets >= WSA_MAXIMUM_WAIT_EVENTS) { + errno = EMFILE; + return -1; + } + + if (nd->readCallback != NULL) + eventMask |= FD_READ | FD_ACCEPT; + + if (nd->writeCallback != NULL) + eventMask |= FD_WRITE /* | FD_CONNECT */; + + if (nd->exceptionCallback != NULL) + eventMask |= FD_OOB; + + eventMask |= FD_CLOSE; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + errno = getWinsockErrno(); + return -1; + } + + nd->smd = SocketManagementData_alloc(); + if (eventMask != 0) { + // XXX: This guard is now always true, because of FD_CLOSE. + // This means that numActiveEvents will always be equal to + // numEvents. + // Once I'm convinced this is the right way to go, + // I can remove some unnecessary code. + if (WSAEventSelect(nd->socket->sock, event, eventMask) == + SOCKET_ERROR) { + int savedErrno = getWinsockErrno(); + int closeStatus = closeWSAEvent(event); + if (closeStatus == -1) { + log_add(log_Fatal, "closeWSAEvent() failed: %s.", + strerror(errno)); + explode(); + } + SocketManagementData_free(nd->smd); + errno = savedErrno; + return -1; + } + + // Move existing socket for which there exists no event, so + // so that all sockets for which there exists an event are at + // the front of the array of netdescriptors. + if (numActiveEvents < numSockets) { + netDescriptors[numSockets] = netDescriptors[numActiveEvents]; + netDescriptors[numSockets]->smd->index = numSockets; + } + + nd->smd->index = numActiveEvents; + numActiveEvents++; + } else { + nd->smd->index = numSockets; + } + nd->smd->eventMask = eventMask; + + netDescriptors[nd->smd->index] = nd; + events[nd->smd->index] = event; + numSockets++; + + return 0; +} + +void +NetManager_removeDesc(NetDescriptor *nd) { + assert(nd->smd != NULL); + assert(nd->smd->index < numSockets); + assert(nd == netDescriptors[nd->smd->index]); + + { + int closeStatus = closeWSAEvent(events[nd->smd->index]); + if (closeStatus == -1) + explode(); + } + + if (nd->smd->index < numActiveEvents) { + size_t index = nd->smd->index; + if (index + 1 != numActiveEvents) { + // Keep the list of active events consecutive by filling + // the new hole with the last active event. + events[index] = events[numActiveEvents - 1]; + netDescriptors[index] = netDescriptors[numActiveEvents - 1]; + netDescriptors[index]->smd->index = index; + } + numActiveEvents--; + } + + SocketManagementData_free(nd->smd); + nd->smd = NULL; + + numSockets--; +} + +static void +swapSockets(int index1, int index2) { + NetDescriptor *tempNd; + WSAEVENT tempEvent; + + tempNd = netDescriptors[index2]; + tempEvent = events[index2]; + + netDescriptors[index2] = netDescriptors[index1]; + events[index2] = events[index1]; + netDescriptors[index2]->smd->index = index2; + + netDescriptors[index1] = tempNd; + events[index1] = tempEvent; + netDescriptors[index1]->smd->index = index1; +} + +static int +NetManager_updateEvent(NetDescriptor *nd) { + assert(nd == netDescriptors[nd->smd->index]); + + if (WSAEventSelect(nd->socket->sock, + events[nd->smd->index], nd->smd->eventMask) == SOCKET_ERROR) { + int savedErrno = getWinsockErrno(); + int closeStatus = closeWSAEvent(events[nd->smd->index]); + if (closeStatus == -1) { + log_add(log_Fatal, "closeWSAEvent() failed: %s.", + strerror(errno)); + explode(); + } + errno = savedErrno; + return -1; + } + + if (nd->smd->eventMask != 0) { + // There are some events that we are interested in. + if (nd->smd->index >= numActiveEvents) { + // Event was not yet active. + if (nd->smd->index != numActiveEvents) { + // Need to keep the active nds and events in the front of + // their arrays. + swapSockets(nd->smd->index, numActiveEvents); + } + numActiveEvents++; + } + } else { + // There are no events that we are interested in. + if (nd->smd->index < numActiveEvents) { + // Event was active. + if (nd->smd->index != numActiveEvents - 1) { + // Need to keep the active nds and events in the front of + // their arrays. + swapSockets(nd->smd->index, numActiveEvents - 1); + } + } + numActiveEvents--; + } + + return 0; +} + +static void +activateSomeCallback(NetDescriptor *nd, long eventMask) { + nd->smd->eventMask |= eventMask; + { + int status = NetManager_updateEvent(nd); + if (status == -1) { + log_add(log_Fatal, "NetManager_updateEvent() failed: %s.", + strerror(errno)); + explode(); + // TODO: better error handling. + } + } +} + +static void +deactivateSomeCallback(NetDescriptor *nd, long eventMask) { + nd->smd->eventMask &= ~eventMask; + { + int status = NetManager_updateEvent(nd); + if (status == -1) { + log_add(log_Fatal, "NetManager_updateEvent() failed: %s.", + strerror(errno)); + explode(); + // TODO: better error handling + } + } +} + +void +NetManager_activateReadCallback(NetDescriptor *nd) { + activateSomeCallback(nd, FD_READ | FD_ACCEPT); +} + +void +NetManager_deactivateReadCallback(NetDescriptor *nd) { + deactivateSomeCallback(nd, FD_READ | FD_ACCEPT); +} + +void +NetManager_activateWriteCallback(NetDescriptor *nd) { + activateSomeCallback(nd, FD_WRITE /* | FD_CONNECT */); +} + +void +NetManager_deactivateWriteCallback(NetDescriptor *nd) { + deactivateSomeCallback(nd, FD_WRITE /* | FD_CONNECT */); +} + +void +NetManager_activateExceptionCallback(NetDescriptor *nd) { + activateSomeCallback(nd, FD_OOB); +} + +void +NetManager_deactivateExceptionCallback(NetDescriptor *nd) { + deactivateSomeCallback(nd, FD_OOB); +} + +static inline int +NetManager_processEvent(size_t index) { + WSANETWORKEVENTS networkEvents; + int enumRes; + + enumRes = WSAEnumNetworkEvents(netDescriptors[index]->socket->sock, + events[index], &networkEvents); + if (enumRes == SOCKET_ERROR) { + errno = getWinsockErrno(); + return -1; + } + + if (networkEvents.lNetworkEvents & FD_READ) { + bool closed; + if (networkEvents.iErrorCode[FD_READ_BIT] != 0) { + // No special handling is required; the callback function + // will try to do a recv() and will get the error then. + } + + closed = NetManager_doReadCallback(netDescriptors[index]); + if (closed) + goto closed; + } + if (networkEvents.lNetworkEvents & FD_WRITE) { + bool closed; + if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) { + // No special handling is required; the callback function + // will try to do a send() and will get the error then. + } + + closed = NetManager_doWriteCallback(netDescriptors[index]); + if (closed) + goto closed; + } + if (networkEvents.lNetworkEvents & FD_OOB) { + bool closed; + if (networkEvents.iErrorCode[FD_OOB_BIT] != 0) { + // No special handling is required; the callback function + // will get the error then when it tries to do a recv(). + } + + closed = NetManager_doExceptionCallback(netDescriptors[index]); + if (closed) + goto closed; + } + if (networkEvents.lNetworkEvents & FD_ACCEPT) { + // There is no specific accept callback (because the BSD sockets + // don't work with specific notification for accept); we use + // the read callback instead. + bool closed; + if (networkEvents.iErrorCode[FD_READ_BIT] != 0) { + // No special handling is required; the callback function + // will try to do an accept() and will get the error then. + } + + closed = NetManager_doReadCallback(netDescriptors[index]); + if (closed) + goto closed; + } +#if 0 + // No need for this. Windows also sets FD_WRITE in this case, and + // writability is what we check for anyhow. + if (networkEvents.lNetworkEvents & FD_CONNECT) { + // There is no specific connect callback (because the BSD sockets + // don't work with specific notification for connect); we use + // the write callback instead. + bool closed; + if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) { + // No special handling is required; the callback function + // should do getsockopt() with SO_ERROR to get the error. + } + + closed = NetManager_doWriteCallback(netDescriptors[index]); + if (closed) + goto closed; + } +#endif + if (networkEvents.lNetworkEvents & FD_CLOSE) { + // The close event is handled last, in case there was still + // data in the buffers which could be processed. + NetDescriptor_close(netDescriptors[index]); + goto closed; + } + +closed: /* No special actions required for now. */ + + return 0; +} + +// This function may be called again from inside a callback function +// triggered by this function. BUG: This may result in callbacks being +// called multiple times. +// This function should however not be called from multiple threads at once. +int +NetManager_process(uint32 *timeoutMs) { + DWORD timeoutTemp; + DWORD waitResult; + DWORD startEvent; + + timeoutTemp = (DWORD) *timeoutMs; + + // WSAWaitForMultipleEvents only reports events for one socket at a + // time. In order to have each socket checked once, we call it + // again after it has reported an event, but passing only the + // events not yet processed. The second time, the timeout will be set + // to 0, so it won't wait. + startEvent = 0; + while (startEvent < numActiveEvents) { + waitResult = WSAWaitForMultipleEvents(numActiveEvents - startEvent, + &events[startEvent], FALSE, timeoutTemp, FALSE); + + if (waitResult == WSA_WAIT_IO_COMPLETION) + continue; + + if (waitResult == WSA_WAIT_TIMEOUT) { + // No events waiting. + *timeoutMs = 0; + return 0; + } + + if (waitResult == WSA_WAIT_FAILED) { + errno = getWinsockErrno(); + *timeoutMs = timeoutTemp; + return -1; + } + + { + DWORD eventIndex = waitResult - WSA_WAIT_EVENT_0; + if (NetManager_processEvent((size_t) eventIndex) == -1) { + // errno is set + *timeoutMs = timeoutTemp; + return -1; + } + + // Check the rest of the sockets, but don't wait anymore. + startEvent += eventIndex + 1; + timeoutTemp = 0; + } + } + + *timeoutMs = timeoutTemp; + return 0; +} + + -- cgit v1.2.3