summaryrefslogtreecommitdiff
path: root/src/libs/network/netmanager
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/network/netmanager')
-rw-r--r--src/libs/network/netmanager/Makeinfo11
-rw-r--r--src/libs/network/netmanager/ndesc.c211
-rw-r--r--src/libs/network/netmanager/ndesc.h82
-rw-r--r--src/libs/network/netmanager/ndindex.ci103
-rw-r--r--src/libs/network/netmanager/netmanager.h48
-rw-r--r--src/libs/network/netmanager/netmanager_bsd.c223
-rw-r--r--src/libs/network/netmanager/netmanager_bsd.h26
-rw-r--r--src/libs/network/netmanager/netmanager_common.ci58
-rw-r--r--src/libs/network/netmanager/netmanager_win.c464
-rw-r--r--src/libs/network/netmanager/netmanager_win.h35
10 files changed, 1261 insertions, 0 deletions
diff --git a/src/libs/network/netmanager/Makeinfo b/src/libs/network/netmanager/Makeinfo
new file mode 100644
index 0000000..ddf28b3
--- /dev/null
+++ b/src/libs/network/netmanager/Makeinfo
@@ -0,0 +1,11 @@
+uqm_CFILES="ndesc.c"
+uqm_HFILES="ndesc.h netmanager.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES netmanager_win.c"
+ uqm_HFILES="$uqm_HFILES netmanager_win.h"
+else
+ uqm_CFILES="$uqm_CFILES netmanager_bsd.c"
+ uqm_HFILES="$uqm_HFILES netmanager_bsd.h"
+fi
+
diff --git a/src/libs/network/netmanager/ndesc.c b/src/libs/network/netmanager/ndesc.c
new file mode 100644
index 0000000..e407ff9
--- /dev/null
+++ b/src/libs/network/netmanager/ndesc.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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 NETDESCRIPTOR_INTERNAL
+#include "ndesc.h"
+
+#include "netmanager.h"
+#include "libs/callback.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#undef DEBUG_NETDESCRIPTOR_REF
+#ifdef DEBUG_NETDESCRIPTOR_REF
+# include "libs/log.h"
+# include <inttypes.h>
+#endif
+
+
+static NetDescriptor *
+NetDescriptor_alloc(void) {
+ return malloc(sizeof (NetDescriptor));
+}
+
+static void
+NetDescriptor_free(NetDescriptor *nd) {
+ free(nd);
+}
+
+// Sets the ref count to 1.
+NetDescriptor *
+NetDescriptor_new(Socket *socket, void *extra) {
+ NetDescriptor *nd;
+
+ nd = NetDescriptor_alloc();
+ nd->refCount = 1;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+
+ nd->flags.closed = false;
+ nd->readCallback = NULL;
+ nd->writeCallback = NULL;
+ nd->exceptionCallback = NULL;
+ nd->closeCallback = NULL;
+ nd->socket = socket;
+ nd->smd = NULL;
+ nd->extra = extra;
+
+ if (NetManager_addDesc(nd) == -1) {
+ int savedErrno = errno;
+ NetDescriptor_free(nd);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ return nd;
+}
+
+static void
+NetDescriptor_delete(NetDescriptor *nd) {
+ assert(nd->socket == Socket_noSocket);
+ assert(nd->smd == NULL);
+
+ NetDescriptor_free(nd);
+}
+
+// Called from the callback handler.
+static void
+NetDescriptor_closeCallback(NetDescriptor *nd) {
+ if (nd->closeCallback != NULL) {
+ // The check is necessary because the close callback may have
+ // been removed before it is triggered.
+ (*nd->closeCallback)(nd);
+ }
+ NetDescriptor_decRef(nd);
+}
+
+void
+NetDescriptor_close(NetDescriptor *nd) {
+ assert(!nd->flags.closed);
+ assert(nd->socket != Socket_noSocket);
+
+ NetManager_removeDesc(nd);
+ (void) Socket_close(nd->socket);
+ nd->socket = Socket_noSocket;
+ nd->flags.closed = true;
+ if (nd->closeCallback != NULL) {
+ // Keep one reference around until the close callback has been
+ // called.
+ (void) Callback_add(
+ (CallbackFunction) NetDescriptor_closeCallback,
+ (CallbackArg) nd);
+ } else
+ NetDescriptor_decRef(nd);
+}
+
+void
+NetDescriptor_incRef(NetDescriptor *nd) {
+ assert(nd->refCount < REFCOUNT_MAX);
+ nd->refCount++;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+}
+
+// returns true iff the ref counter has reached 0.
+bool
+NetDescriptor_decRef(NetDescriptor *nd) {
+ assert(nd->refCount > 0);
+ nd->refCount--;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+ if (nd->refCount == 0) {
+ NetDescriptor_delete(nd);
+ return true;
+ }
+ return false;
+}
+
+// The socket will no longer be managed by the NetManager.
+void
+NetDescriptor_detach(NetDescriptor *nd) {
+ NetManager_removeDesc(nd);
+ nd->socket = Socket_noSocket;
+ nd->flags.closed = true;
+ NetDescriptor_decRef(nd);
+}
+
+Socket *
+NetDescriptor_getSocket(NetDescriptor *nd) {
+ return nd->socket;
+}
+
+void
+NetDescriptor_setExtra(NetDescriptor *nd, void *extra) {
+ nd->extra = extra;
+}
+
+void *
+NetDescriptor_getExtra(const NetDescriptor *nd) {
+ return nd->extra;
+}
+
+void
+NetDescriptor_setReadCallback(NetDescriptor *nd,
+ NetDescriptor_ReadCallback callback) {
+ nd->readCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->readCallback != NULL) {
+ NetManager_activateReadCallback(nd);
+ } else
+ NetManager_deactivateReadCallback(nd);
+ }
+}
+
+void
+NetDescriptor_setWriteCallback(NetDescriptor *nd,
+ NetDescriptor_WriteCallback callback) {
+ nd->writeCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->writeCallback != NULL) {
+ NetManager_activateWriteCallback(nd);
+ } else
+ NetManager_deactivateWriteCallback(nd);
+ }
+}
+
+void
+NetDescriptor_setExceptionCallback(NetDescriptor *nd,
+ NetDescriptor_ExceptionCallback callback) {
+ nd->exceptionCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->exceptionCallback != NULL) {
+ NetManager_activateExceptionCallback(nd);
+ } else
+ NetManager_deactivateExceptionCallback(nd);
+ }
+}
+
+// The close callback is called as a result of a socket being closed, either
+// because of a local command or a remote disconnect.
+// The close callback will only be scheduled when this happens. The
+// callback will not be called until the Callback_process() is called.
+void
+NetDescriptor_setCloseCallback(NetDescriptor *nd,
+ NetDescriptor_CloseCallback callback) {
+ nd->closeCallback = callback;
+}
+
+
diff --git a/src/libs/network/netmanager/ndesc.h b/src/libs/network/netmanager/ndesc.h
new file mode 100644
index 0000000..8834db9
--- /dev/null
+++ b/src/libs/network/netmanager/ndesc.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NDESC_H_
+#define LIBS_NETWORK_NETMANAGER_NDESC_H_
+
+#include "types.h"
+
+
+typedef struct NetDescriptor NetDescriptor;
+
+typedef void (*NetDescriptor_ReadCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_WriteCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_ExceptionCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_CloseCallback)(NetDescriptor *nd);
+
+typedef uint32 RefCount;
+#define REFCOUNT_MAX UINT32_MAX
+
+#include "../socket/socket.h"
+#include "netmanager.h"
+
+#ifdef NETDESCRIPTOR_INTERNAL
+// All created NetDescriptors are registered to the NetManager.
+// They are unregisted when the NetDescriptor is closed.
+// On creation the ref count is set to 1. On close it is decremented by 1.
+struct NetDescriptor {
+ struct {
+ bool closed: 1;
+ } flags;
+
+ RefCount refCount;
+
+ NetDescriptor_ReadCallback readCallback;
+ NetDescriptor_WriteCallback writeCallback;
+ NetDescriptor_ExceptionCallback exceptionCallback;
+ NetDescriptor_CloseCallback closeCallback;
+
+ Socket *socket;
+ SocketManagementData *smd;
+
+ // Extra state-dependant information for the user.
+ void *extra;
+};
+#endif
+
+NetDescriptor *NetDescriptor_new(Socket *socket, void *extra);
+void NetDescriptor_close(NetDescriptor *nd);
+void NetDescriptor_incRef(NetDescriptor *nd);
+bool NetDescriptor_decRef(NetDescriptor *nd);
+void NetDescriptor_detach(NetDescriptor *nd);
+Socket *NetDescriptor_getSocket(NetDescriptor *nd);
+void NetDescriptor_setExtra(NetDescriptor *nd, void *extra);
+void *NetDescriptor_getExtra(const NetDescriptor *nd);
+void NetDescriptor_setReadCallback(NetDescriptor *nd,
+ NetDescriptor_ReadCallback callback);
+void NetDescriptor_setWriteCallback(NetDescriptor *nd,
+ NetDescriptor_WriteCallback callback);
+void NetDescriptor_setExceptionCallback(NetDescriptor *nd,
+ NetDescriptor_ExceptionCallback callback);
+void NetDescriptor_setCloseCallback(NetDescriptor *nd,
+ NetDescriptor_CloseCallback callback);
+
+
+#endif /* LIBS_NETWORK_NETMANAGER_NDESC_H_ */
+
+
diff --git a/src/libs/network/netmanager/ndindex.ci b/src/libs/network/netmanager/ndindex.ci
new file mode 100644
index 0000000..f922d8b
--- /dev/null
+++ b/src/libs/network/netmanager/ndindex.ci
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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
+ */
+
+// This file is part of netmanager_bsd.c, from where it is #included.
+// Only used for BSD sockets.
+
+// This file provides a mapping of Sockets to NetDescriptors.
+
+
+static NetDescriptor *netDescriptors[FD_SETSIZE];
+ // INV: flags.closed is not set for entries in netDescriptors.
+static size_t maxND;
+ // One past the largest used ND in netDescriptors, as used in
+ // the first argument of select();
+
+
+static inline void
+NDIndex_init(void) {
+ size_t i;
+ size_t numND = sizeof (netDescriptors) / sizeof (netDescriptors[0]);
+
+ for (i = 0; i < numND; i++)
+ netDescriptors[i] = NULL;
+}
+
+static inline void
+NDIndex_uninit(void) {
+ // Nothing to do.
+}
+
+static inline int
+NDIndex_registerNDWithSocket(Socket *sock, NetDescriptor *nd) {
+ if ((unsigned int) sock->fd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ netDescriptors[sock->fd] = nd;
+
+ if ((size_t) sock->fd >= maxND)
+ maxND = (size_t) sock->fd + 1;
+
+ return 0;
+}
+
+static inline void
+NDIndex_unregisterNDForSocket(Socket *sock) {
+ NetDescriptor **last;
+
+ netDescriptors[sock->fd] = NULL;
+
+ last = &netDescriptors[sock->fd];
+
+ if ((size_t) sock->fd + 1 == maxND) {
+ do {
+ maxND--;
+ if (last == &netDescriptors[0])
+ break;
+ last--;
+ } while (*last == NULL);
+ }
+}
+
+static inline NetDescriptor *
+NDIndex_getNDForSocket(Socket *sock) {
+ assert((size_t) sock->fd < maxND);
+ return netDescriptors[sock->fd];
+}
+
+static inline NetDescriptor *
+NDIndex_getNDForSocketFd(int fd) {
+ assert((size_t) fd < maxND);
+ return netDescriptors[fd];
+}
+
+static inline bool
+NDIndex_socketRegistered(Socket *sock) {
+ return ((size_t) sock->fd < maxND)
+ && (NDIndex_getNDForSocket(sock) != NULL);
+}
+
+// Get the first argument to be used in select().
+static inline size_t
+NDIndex_getSelectNumND(void) {
+ return maxND;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager.h b/src/libs/network/netmanager/netmanager.h
new file mode 100644
index 0000000..42be572
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_H_
+
+#include "port.h"
+#include "types.h"
+
+#ifdef USE_WINSOCK
+# include "netmanager_win.h"
+#else
+# include "netmanager_bsd.h"
+#endif
+
+#include "ndesc.h"
+
+void NetManager_init(void);
+void NetManager_uninit(void);
+int NetManager_process(uint32 *timeoutMs);
+
+// Only for internal use by the NetManager:
+int NetManager_addDesc(NetDescriptor *nd);
+void NetManager_removeDesc(NetDescriptor *nd);
+void NetManager_activateReadCallback(NetDescriptor *nd);
+void NetManager_deactivateReadCallback(NetDescriptor *nd);
+void NetManager_activateWriteCallback(NetDescriptor *nd);
+void NetManager_deactivateWriteCallback(NetDescriptor *nd);
+void NetManager_activateExceptionCallback(NetDescriptor *nd);
+void NetManager_deactivateExceptionCallback(NetDescriptor *nd);
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_H_ */
+
diff --git a/src/libs/network/netmanager/netmanager_bsd.c b/src/libs/network/netmanager/netmanager_bsd.c
new file mode 100644
index 0000000..29159f8
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_bsd.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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 SOCKET_INTERNAL
+#define NETDESCRIPTOR_INTERNAL
+#include "netmanager_bsd.h"
+#include "ndesc.h"
+#include "../socket/socket.h"
+
+#include "ndesc.h"
+#include "types.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "netmanager_common.ci"
+#include "ndindex.ci"
+
+
+// INV: The following sets only contain sockets present in the netDescriptor
+// array.
+static fd_set readSet;
+static fd_set writeSet;
+static fd_set exceptionSet;
+
+
+void
+NetManager_init(void) {
+ NDIndex_init();
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptionSet);
+}
+
+void
+NetManager_uninit(void) {
+ NDIndex_uninit();
+}
+
+// Register the NetDescriptor with the NetManager.
+int
+NetManager_addDesc(NetDescriptor *nd) {
+ int fd;
+ assert(nd->socket != Socket_noSocket);
+ assert(!NDIndex_socketRegistered(nd->socket));
+
+ if (NDIndex_registerNDWithSocket(nd->socket, nd) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ fd = nd->socket->fd;
+ if (nd->readCallback != NULL)
+ FD_SET(fd, &readSet);
+ if (nd->writeCallback != NULL)
+ FD_SET(fd, &writeSet);
+ if (nd->exceptionCallback != NULL)
+ FD_SET(fd, &exceptionSet);
+ return 0;
+}
+
+void
+NetManager_removeDesc(NetDescriptor *nd) {
+ int fd;
+
+ assert(nd->socket != Socket_noSocket);
+ assert(NDIndex_getNDForSocket(nd->socket) == nd);
+
+ fd = nd->socket->fd;
+ FD_CLR(fd, &readSet);
+ FD_CLR(fd, &writeSet);
+ FD_CLR(fd, &exceptionSet);
+
+ NDIndex_unregisterNDForSocket(nd->socket);
+}
+
+void
+NetManager_activateReadCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &readSet);
+}
+
+void
+NetManager_deactivateReadCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &readSet);
+}
+
+void
+NetManager_activateWriteCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &writeSet);
+}
+
+void
+NetManager_deactivateWriteCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &writeSet);
+}
+
+void
+NetManager_activateExceptionCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &exceptionSet);
+}
+
+void
+NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &exceptionSet);
+}
+
+// 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) {
+ struct timeval timeout;
+ size_t i;
+ int selectResult;
+ fd_set newReadSet;
+ fd_set newWriteSet;
+ fd_set newExceptionSet;
+ bool bitSet;
+
+ timeout.tv_sec = *timeoutMs / 1000;
+ timeout.tv_usec = (*timeoutMs % 1000) * 1000;
+
+ // Structure assignment:
+ newReadSet = readSet;
+ newWriteSet = writeSet;
+ newExceptionSet = exceptionSet;
+
+ do {
+ selectResult = select(NDIndex_getSelectNumND(),
+ &newReadSet, &newWriteSet, &newExceptionSet, &timeout);
+ // BUG: If select() is restarted because of EINTR, the timeout
+ // may start over. (Linux changes 'timeout' to the time left,
+ // but most other platforms don't.)
+ } while (selectResult == -1 && errno == EINTR);
+ if (selectResult == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "select() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
+ // XXX: rounding microseconds down. Is that the correct
+ // thing to do?
+ return -1;
+ }
+
+ for (i = 0; i < maxND; i++) {
+ NetDescriptor *nd;
+
+ if (selectResult == 0) {
+ // No more bits set in the fd_sets
+ break;
+ }
+
+ nd = NDIndex_getNDForSocketFd(i);
+ if (nd == NULL)
+ continue;
+
+ bitSet = false;
+ // Is one of the bits in the fd_sets set?
+
+ // A callback may cause a NetDescriptor to be closed. The deletion
+ // of the structure will be scheduled, but will still be
+ // available at least until this function returns.
+
+ if (FD_ISSET(i, &newExceptionSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doExceptionCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+ if (FD_ISSET(i, &newWriteSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doWriteCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+ if (FD_ISSET(i, &newReadSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doReadCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+next:
+ if (bitSet)
+ selectResult--;
+ }
+
+ *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
+ // XXX: rounding microseconds down. Is that the correct
+ // thing to do?
+ return 0;
+}
+
+
+
diff --git a/src/libs/network/netmanager/netmanager_bsd.h b/src/libs/network/netmanager/netmanager_bsd.h
new file mode 100644
index 0000000..b94dd69
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_bsd.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_
+
+typedef struct SocketManagementDataBsd SocketManagementDataBsd;
+typedef SocketManagementDataBsd SocketManagementData;
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_ */
+
diff --git a/src/libs/network/netmanager/netmanager_common.ci b/src/libs/network/netmanager/netmanager_common.ci
new file mode 100644
index 0000000..058e739
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_common.ci
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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
+ */
+
+// This file is part of netmanager_bsd.ci and netmanager_win.ci,
+// from where it is #included.
+
+static bool
+NetManager_doReadCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->readCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->readCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+static bool
+NetManager_doWriteCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->writeCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->writeCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+static bool
+NetManager_doExceptionCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->exceptionCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->exceptionCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+
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 <svdb@stack.nl>
+ *
+ * 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 <assert.h>
+#include <winsock2.h>
+
+#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;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager_win.h b/src/libs/network/netmanager/netmanager_win.h
new file mode 100644
index 0000000..03d65e4
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_win.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * 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
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_
+
+typedef struct SocketManagementDataWin SocketManagementDataWin;
+typedef SocketManagementDataWin SocketManagementData;
+
+#ifdef NETMANAGER_INTERNAL
+struct SocketManagementDataWin {
+ size_t index;
+ long eventMask;
+};
+#endif /* NETMANAGER_INTERNAL */
+
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_ */
+
+