aboutsummaryrefslogtreecommitdiff
path: root/src/platform
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2017-07-07 18:17:51 -0700
committerGarrett D'Amore <garrett@damore.org>2017-07-07 18:17:51 -0700
commit021d09af1375834fe9aaca916e60dcd2ad3be845 (patch)
treee3a4c8d2e0eddd69b79308b67922725901752ee9 /src/platform
parentc6df64d90ff9799b9f5970c4897e2b9a55e2ce69 (diff)
downloadnng-021d09af1375834fe9aaca916e60dcd2ad3be845.tar.gz
nng-021d09af1375834fe9aaca916e60dcd2ad3be845.tar.bz2
nng-021d09af1375834fe9aaca916e60dcd2ad3be845.zip
Start of progress on Windows. Name resolution and IOCP work begins.
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/posix/posix_config.h4
-rw-r--r--src/platform/posix/posix_pollq.h4
-rw-r--r--src/platform/windows/win_debug.c49
-rw-r--r--src/platform/windows/win_impl.h32
-rw-r--r--src/platform/windows/win_iocp.c145
-rw-r--r--src/platform/windows/win_ipc.c129
-rw-r--r--src/platform/windows/win_net.c113
-rw-r--r--src/platform/windows/win_resolv.c295
-rw-r--r--src/platform/windows/win_thread.c18
9 files changed, 724 insertions, 65 deletions
diff --git a/src/platform/posix/posix_config.h b/src/platform/posix/posix_config.h
index d2a5eb90..1fe4d1a9 100644
--- a/src/platform/posix/posix_config.h
+++ b/src/platform/posix/posix_config.h
@@ -42,6 +42,8 @@
#include <time.h>
+#ifdef PLATFORM_POSIX
+
// It should never hurt to use DEVURANDOM, since if the device does not
// exist then we won't open it. (Provided: it would be bad if the device
// exists but has somehow very very different semantics. We don't know
@@ -60,3 +62,5 @@
#define NNG_USE_POSIX_POLLQ_POLL 1
#define NNG_USE_POSIX_RESOLV_GAI 1
+
+#endif // PLATFORM_POSIX \ No newline at end of file
diff --git a/src/platform/posix/posix_pollq.h b/src/platform/posix/posix_pollq.h
index 74b39704..a04d1ef2 100644
--- a/src/platform/posix/posix_pollq.h
+++ b/src/platform/posix/posix_pollq.h
@@ -10,6 +10,8 @@
#ifndef PLATFORM_POSIX_POLLQ_H
#define PLATFORM_POSIX_POLLQ_H
+#ifdef PLATFORM_POSIX
+
// This file defines structures we will use for emulating asynchronous I/O
// on POSIX. POSIX lacks the support for callback based asynchronous I/O
// that we have on Windows, although it has a non-widely support aio layer
@@ -40,4 +42,6 @@ extern void nni_posix_pollq_cancel(nni_posix_pollq *, nni_posix_pollq_node *);
extern int nni_posix_pollq_sysinit(void);
extern void nni_posix_pollq_sysfini(void);
+#endif // PLATFORM_POSIX
+
#endif // PLATFORM_POSIX_POLLQ_H
diff --git a/src/platform/windows/win_debug.c b/src/platform/windows/win_debug.c
index cf8ad9eb..a4edef0d 100644
--- a/src/platform/windows/win_debug.c
+++ b/src/platform/windows/win_debug.c
@@ -77,6 +77,55 @@ nni_plat_errno(int errnum)
return (NNG_ESYSERR + errnum);
}
+// Windows has infinite numbers of error codes it seems. We only bother
+// with the ones that are relevant to us (we think). Note that there is
+// no overlap between errnos and GetLastError values.
+static struct {
+ int win_err;
+ int nng_err;
+}
+nni_win_errnos[] = {
+ { ERROR_FILE_NOT_FOUND, NNG_ENOENT },
+ { ERROR_ACCESS_DENIED, NNG_EPERM },
+ { ERROR_INVALID_HANDLE, NNG_ECLOSED },
+ { ERROR_NOT_ENOUGH_MEMORY, NNG_ENOMEM },
+ { ERROR_INVALID_ACCESS, NNG_EPERM },
+ { ERROR_INVALID_DATA, NNG_EINVAL },
+ { ERROR_OUTOFMEMORY, NNG_ENOMEM },
+ { ERROR_HANDLE_EOF, NNG_ECLOSED },
+ { ERROR_NOT_SUPPORTED, NNG_ENOTSUP },
+ { ERROR_OUT_OF_STRUCTURES, NNG_ENOMEM },
+ { ERROR_INVALID_PARAMETER, NNG_EINVAL },
+ { ERROR_CONNECTION_REFUSED, NNG_ECONNREFUSED },
+ { ERROR_BROKEN_PIPE, NNG_ECLOSED },
+ { ERROR_BAD_PIPE, NNG_ECLOSED },
+ { ERROR_NO_DATA, NNG_ECLOSED },
+ { ERROR_PIPE_NOT_CONNECTED, NNG_ECLOSED },
+ { ERROR_OPERATION_ABORTED, NNG_ECLOSED },
+ { WAIT_TIMEOUT, NNG_ETIMEDOUT },
+ // Must be Last!!
+ { 0, 0 },
+};
+
+// This converts a Windows API error (from GetLastError()) to an
+// nng standard error code.
+int
+nni_win_error(int errnum)
+{
+ int i;
+
+ if (errnum == 0) {
+ return (0);
+ }
+ for (i = 0; nni_win_errnos[i].nng_err != 0; i++) {
+ if (errnum == nni_win_errnos[i].win_err) {
+ return (nni_win_errnos[i].nng_err);
+ }
+ }
+ // Other system errno.
+ return (NNG_ESYSERR + errnum);
+}
+
#else
diff --git a/src/platform/windows/win_impl.h b/src/platform/windows/win_impl.h
index 7bb71707..85f73bf7 100644
--- a/src/platform/windows/win_impl.h
+++ b/src/platform/windows/win_impl.h
@@ -26,18 +26,19 @@
// These types are provided for here, to permit them to be directly inlined
// elsewhere.
-struct nni_plat_tcpsock {
- SOCKET s;
-
- WSAOVERLAPPED recv_olpd;
- WSAOVERLAPPED send_olpd;
- WSAOVERLAPPED conn_olpd; // Use for both connect and accept
- int family;
-
- // We have to lookup some function pointers using ioctls. Winsock,
- // gotta love it.
- LPFN_CONNECTEX connectex;
- LPFN_ACCEPTEX acceptex;
+typedef struct nni_win_event nni_win_event;
+
+// nni_win_event is used with io completion ports. This allows us to get
+// to a specific completion callback without requiring the poller (in the
+// completion port) to know anything about the event itself. We also use
+// this to pass back status and counts to the routine, which may not be
+// conveyed in the OVERLAPPED directly.
+struct nni_win_event {
+ OVERLAPPED olpd;
+ void * ptr;
+ nni_cb cb;
+ int status;
+ int nbytes;
};
struct nni_plat_ipcsock {
@@ -69,8 +70,15 @@ struct nni_plat_cv {
CRITICAL_SECTION * cs;
};
+extern int nni_win_error(int);
extern int nni_winsock_error(int);
+extern int nni_win_iocp_sysinit(void);
+extern void nni_win_iocp_sysfini(void);
+
+extern int nni_win_resolv_sysinit(void);
+extern void nni_win_resolv_sysfini(void);
+
#endif // PLATFORM_WINDOWS
#endif // PLATFORM_WIN_IMPL_H
diff --git a/src/platform/windows/win_iocp.c b/src/platform/windows/win_iocp.c
new file mode 100644
index 00000000..06feb62e
--- /dev/null
+++ b/src/platform/windows/win_iocp.c
@@ -0,0 +1,145 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include "core/nng_impl.h"
+
+#ifdef PLATFORM_WINDOWS
+
+#define NNI_WIN_IOCP_NTHREADS 4
+#include <stdio.h>
+
+// Windows IO Completion Port support. We basically creaet a single
+// IO completion port, then start threads on it. Handles are added
+// to the port on an as needed basis. We use a single IO completion
+// port for pretty much everything.
+
+static HANDLE nni_win_global_iocp = NULL;
+static nni_win_event nni_win_iocp_exit;
+static nni_thr nni_win_iocp_thrs[NNI_WIN_IOCP_NTHREADS];
+
+static void
+nni_win_iocp_handler(void *arg)
+{
+ HANDLE iocp;
+ DWORD nbytes;
+ ULONG_PTR key;
+ OVERLAPPED *olpd;
+ nni_win_event *evt;
+ int status;
+ BOOL rv;
+
+ NNI_ARG_UNUSED(arg);
+
+ iocp = nni_win_global_iocp;
+
+ for (;;) {
+ key = 0;
+ olpd = NULL;
+ status = 0;
+ rv = GetQueuedCompletionStatus(iocp, &nbytes, &key, &olpd,
+ INFINITE);
+
+ if (rv == FALSE) {
+ if (olpd == NULL) {
+ // Completion port bailed...
+ break;
+ }
+ nbytes = 0;
+ key = 0;
+ status = nni_win_error(GetLastError());
+ }
+
+ NNI_ASSERT(olpd != NULL);
+ evt = (void *) olpd;
+ if (evt == &nni_win_iocp_exit) {
+ // Exit requested.
+ break;
+ }
+
+ NNI_ASSERT(evt->cb != NULL);
+ evt->nbytes = nbytes;
+ evt->status = status;
+ evt->cb(evt->ptr);
+ }
+}
+
+
+int
+nni_win_iocp_register(HANDLE h)
+{
+ if (CreateIoCompletionPort(h, nni_win_global_iocp, 0, 0) == NULL) {
+ return (nni_win_error(GetLastError()));
+ }
+ return (0);
+}
+
+
+int
+nni_win_iocp_sysinit(void)
+{
+ HANDLE h;
+ int i;
+ int rv;
+
+ ZeroMemory(&nni_win_iocp_exit, sizeof (nni_win_iocp_exit));
+ h = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
+ NNI_WIN_IOCP_NTHREADS);
+ if (h == NULL) {
+ return (nni_win_error(GetLastError()));
+ }
+ nni_win_global_iocp = h;
+ for (i = 0; i < NNI_WIN_IOCP_NTHREADS; i++) {
+ rv = nni_thr_init(&nni_win_iocp_thrs[i], nni_win_iocp_handler,
+ NULL);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+ for (i = 0; i < NNI_WIN_IOCP_NTHREADS; i++) {
+ nni_thr_run(&nni_win_iocp_thrs[i]);
+ }
+ return (0);
+
+fail:
+ if ((h = nni_win_global_iocp) != NULL) {
+ PostQueuedCompletionStatus(h, 0, 0, &nni_win_iocp_exit.olpd);
+ CloseHandle(h);
+ nni_win_global_iocp = NULL;
+ }
+ for (i = 0; i < NNI_WIN_IOCP_NTHREADS; i++) {
+ nni_thr_fini(&nni_win_iocp_thrs[i]);
+ }
+ return (rv);
+}
+
+
+void
+nni_win_iocp_sysfini(void)
+{
+ int i;
+ HANDLE h;
+
+ if ((h = nni_win_global_iocp) != NULL) {
+ // Signal the iocp poller to exit.
+ PostQueuedCompletionStatus(h, 0, 0, &nni_win_iocp_exit.olpd);
+ CloseHandle(h);
+ nni_win_global_iocp = NULL;
+ for (i = 0; i < NNI_WIN_IOCP_NTHREADS; i++) {
+ nni_thr_fini(&nni_win_iocp_thrs[i]);
+ }
+ }
+}
+
+
+#else
+
+// Suppress empty symbols warnings in ranlib.
+int nni_win_iocp_not_used = 0;
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_ipc.c b/src/platform/windows/win_ipc.c
index 86a4be03..8ae26a6e 100644
--- a/src/platform/windows/win_ipc.c
+++ b/src/platform/windows/win_ipc.c
@@ -13,54 +13,7 @@
#include <stdio.h>
-// Windows has infinite numbers of error codes it seems. We only bother
-// with the ones that are relevant to us (we think).
-static struct {
- int win_err;
- int nng_err;
-}
-nni_winpipe_errnos[] = {
- { ERROR_FILE_NOT_FOUND, NNG_ENOENT },
- { ERROR_ACCESS_DENIED, NNG_EPERM },
- { ERROR_INVALID_HANDLE, NNG_ECLOSED },
- { ERROR_NOT_ENOUGH_MEMORY, NNG_ENOMEM },
- { ERROR_INVALID_ACCESS, NNG_EPERM },
- { ERROR_INVALID_DATA, NNG_EINVAL },
- { ERROR_OUTOFMEMORY, NNG_ENOMEM },
- { ERROR_HANDLE_EOF, NNG_ECLOSED },
- { ERROR_NOT_SUPPORTED, NNG_ENOTSUP },
- { ERROR_OUT_OF_STRUCTURES, NNG_ENOMEM },
- { ERROR_INVALID_PARAMETER, NNG_EINVAL },
- { ERROR_CONNECTION_REFUSED, NNG_ECONNREFUSED },
- { ERROR_BROKEN_PIPE, NNG_ECLOSED },
- { ERROR_BAD_PIPE, NNG_ECLOSED },
- { ERROR_NO_DATA, NNG_ECLOSED },
- { ERROR_PIPE_NOT_CONNECTED, NNG_ECLOSED },
- { ERROR_OPERATION_ABORTED, NNG_ECLOSED },
- { WAIT_TIMEOUT, NNG_ETIMEDOUT },
- // Must be Last!!
- { 0, 0 },
-};
-
-
-static int
-nni_winpipe_error(int werr)
-{
- int i;
-
- if (werr == 0) {
- return (0);
- }
-
- for (i = 0; nni_winpipe_errnos[i].nng_err != 0; i++) {
- if (werr == nni_winpipe_errnos[i].win_err) {
- return (nni_winpipe_errnos[i].nng_err);
- }
- }
- // Other system errno.
- return (NNG_ESYSERR + werr);
-}
-
+#if 0
int
nni_plat_ipc_send(nni_plat_ipcsock *s, nni_iov *iovs, int cnt)
@@ -348,6 +301,86 @@ nni_plat_ipc_connect(nni_plat_ipcsock *s, const char *path)
}
+#endif
+
+
+struct nni_plat_ipc_pipe {
+ SOCKET s;
+ nni_win_event recv_evt;
+ nni_win_event send_evt;
+ OVERLAPPED recv_olpd;
+ OVERLAPPED send_olpd;
+};
+
+struct nni_plat_ipc_ep {
+ SOCKET s;
+ nni_win_event evt;
+ OVERLAPPED olpd;
+};
+
+
+int
+nni_plat_ipc_ep_init(nni_plat_ipc_ep **epp, const char *url, int mode)
+{
+ return (NNG_ENOTSUP);
+}
+
+
+void
+nni_plat_ipc_ep_fini(nni_plat_ipc_ep *ep)
+{
+}
+
+
+void
+nni_plat_ipc_ep_close(nni_plat_ipc_ep *ep)
+{
+}
+
+
+extern int
+nni_plat_ipc_ep_listen(nni_plat_ipc_ep *ep)
+{
+ return (NNG_ENOTSUP);
+}
+
+
+extern void
+nni_plat_ipc_ep_accept(nni_plat_ipc_ep *ep, nni_aio *aio)
+{
+}
+
+
+extern void
+nni_plat_ipc_ep_connect(nni_plat_ipc_ep *ep, nni_aio *aio)
+{
+}
+
+
+void
+nni_plat_ipc_pipe_send(nni_plat_ipc_pipe *p, nni_aio *aio)
+{
+}
+
+
+void
+nni_plat_ipc_pipe_recv(nni_plat_ipc_pipe *p, nni_aio *aio)
+{
+}
+
+
+void
+nni_plat_ipc_pipe_close(nni_plat_ipc_pipe *p)
+{
+}
+
+
+void
+nni_plat_ipc_pipe_fini(nni_plat_ipc_pipe *p)
+{
+}
+
+
#else
// Suppress empty symbols warnings in ranlib.
diff --git a/src/platform/windows/win_net.c b/src/platform/windows/win_net.c
index 678ccfb9..2a77d97f 100644
--- a/src/platform/windows/win_net.c
+++ b/src/platform/windows/win_net.c
@@ -13,6 +13,31 @@
#include <stdio.h>
+static LPFN_CONNECTEX nni_win_connectex;
+static LPFN_ACCEPTEX nni_win_acceptex;
+
+
+struct nni_plat_tcp_pipe {
+ SOCKET s;
+ nni_win_event recv_evt;
+ nni_win_event send_evt;
+ WSAOVERLAPPED recv_olpd;
+ WSAOVERLAPPED send_olpd;
+};
+
+
+struct nni_plat_tcp_ep {
+ SOCKET s;
+ nni_win_event evt;
+ WSAOVERLAPPED olpd;
+
+ // We have to lookup some function pointers using ioctls. Winsock,
+ // gotta love it.
+ LPFN_CONNECTEX connectex;
+ LPFN_ACCEPTEX acceptex;
+};
+
+
// Windows has infinite numbers of error codes it seems.
static struct {
int wsa_err;
@@ -197,7 +222,88 @@ nni_plat_lookup_host(const char *host, nni_sockaddr *addr, int flags)
int
-nni_plat_tcp_send(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
+nni_plat_tcp_ep_init(nni_plat_tcp_ep **epp, const char *url, int mode)
+{
+ nni_plat_tcp_ep *ep;
+
+ if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ ZeroMemory(ep, sizeof (*ep));
+ ep->s = INVALID_SOCKET;
+
+ // XXX: no need to create event?
+ ep->olpd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (ep->olpd.hEvent == INVALID_HANDLE_VALUE) {
+ return (nni_win_error(GetLastError()));
+ }
+
+ // XXX: save URL, do lookups? etc. etc.
+
+ *epp = ep;
+ return (NNG_ENOTSUP);
+}
+
+
+void
+nni_plat_tcp_ep_fini(nni_plat_tcp_ep *ep)
+{
+}
+
+
+void
+nni_plat_tcp_ep_close(nni_plat_tcp_ep *ep)
+{
+}
+
+
+extern int
+nni_plat_tcp_ep_listen(nni_plat_tcp_ep *ep)
+{
+ return (NNG_ENOTSUP);
+}
+
+
+extern void
+nni_plat_tcp_ep_accept(nni_plat_tcp_ep *ep, nni_aio *aio)
+{
+}
+
+
+extern void
+nni_plat_tcp_ep_connect(nni_plat_tcp_ep *ep, nni_aio *aio)
+{
+}
+
+
+void
+nni_plat_tcp_pipe_send(nni_plat_tcp_pipe *p, nni_aio *aio)
+{
+}
+
+
+void
+nni_plat_tcp_pipe_recv(nni_plat_tcp_pipe *p, nni_aio *aio)
+{
+}
+
+
+void
+nni_plat_tcp_pipe_close(nni_plat_tcp_pipe *p)
+{
+}
+
+
+void
+nni_plat_tcp_pipe_fini(nni_plat_tcp_pipe *p)
+{
+}
+
+
+#if 0
+
+int
+nni_plat_tcp_send_old(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
{
WSABUF iov[4]; // We never have more than 3 at present
int i;
@@ -253,7 +359,7 @@ nni_plat_tcp_send(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
int
-nni_plat_tcp_recv(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
+nni_plat_tcp_recv_old(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
{
WSABUF iov[4]; // We never have more than 3 at present
int i;
@@ -572,6 +678,9 @@ nni_plat_tcp_accept(nni_plat_tcpsock *s, nni_plat_tcpsock *server)
}
+#endif
+
+
#else
// Suppress empty symbols warnings in ranlib.
diff --git a/src/platform/windows/win_resolv.c b/src/platform/windows/win_resolv.c
new file mode 100644
index 00000000..3d37d546
--- /dev/null
+++ b/src/platform/windows/win_resolv.c
@@ -0,0 +1,295 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+
+#include "core/nng_impl.h"
+
+#ifdef PLATFORM_WINDOWS
+
+// Modern Windows has an asynchronous resolver, but there are problems
+// with it -- it only resolves Unicode names, and it does not support
+
+// We use a single resolver taskq - but we allocate a few threads
+// for it to ensure that names can be looked up concurrently. This isn't
+// as elegant or scaleable as a true asynchronous resolver would be, but
+// it has the advantage of being fairly portable, and concurrent enough for
+// the vast, vast majority of use cases. The total thread count can be
+// changed with this define.
+
+#ifndef NNG_WIN_RESOLV_CONCURRENCY
+#define NNG_WIN_RESOLV_CONCURRENCY 4
+#endif
+
+
+static nni_taskq *nni_win_resolv_tq = NULL;
+static nni_mtx nni_win_resolv_mtx;
+
+typedef struct nni_win_resolv_item nni_win_resolv_item;
+struct nni_win_resolv_item {
+ int family;
+ int passive;
+ const char * name;
+ const char * serv;
+ int proto;
+ nni_aio * aio;
+ nni_taskq_ent tqe;
+};
+
+
+static void
+nni_win_resolv_finish(nni_win_resolv_item *item, int rv)
+{
+ nni_aio *aio = item->aio;
+
+ aio->a_prov_data = NULL;
+ nni_aio_finish(aio, rv, 0);
+ NNI_FREE_STRUCT(item);
+}
+
+
+static void
+nni_win_resolv_cancel(nni_aio *aio)
+{
+ nni_win_resolv_item *item;
+
+ nni_mtx_lock(&nni_win_resolv_mtx);
+ if ((item = aio->a_prov_data) == NULL) {
+ nni_mtx_unlock(&nni_win_resolv_mtx);
+ return;
+ }
+ aio->a_prov_data = NULL;
+ nni_mtx_unlock(&nni_win_resolv_mtx);
+ nni_taskq_cancel(nni_win_resolv_tq, &item->tqe);
+ NNI_FREE_STRUCT(item);
+}
+
+
+static int
+nni_win_gai_errno(int rv)
+{
+ switch (rv) {
+ case 0:
+ return (0);
+
+ case WSA_NOT_ENOUGH_MEMORY:
+ return (NNG_ENOMEM);
+
+ case WSAHOST_NOT_FOUND:
+ case WSATYPE_NOT_FOUND:
+ case WSANO_DATA:
+ return (NNG_EADDRINVAL);
+
+ case WSAEINVAL:
+ return (NNG_EINVAL);
+
+ case WSAESOCKTNOSUPPORT:
+ case WSAEAFNOSUPPORT:
+ return (NNG_ENOTSUP);
+
+ default:
+ return (NNG_ESYSERR + rv);
+ }
+}
+
+
+static void
+nni_win_resolv_task(void *arg)
+{
+ nni_win_resolv_item *item = arg;
+ nni_aio *aio = item->aio;
+ struct addrinfo hints;
+ struct addrinfo *results;
+ struct addrinfo *probe;
+ int i, rv;
+
+ results = NULL;
+
+ switch (item->family) {
+ case AF_INET:
+ case AF_INET6:
+ case AF_UNSPEC:
+ // We treat these all as IP addresses. The service and the
+ // host part are split.
+ memset(&hints, 0, sizeof (hints));
+ if (item->passive) {
+ hints.ai_flags |= AI_PASSIVE;
+ }
+ hints.ai_flags |= AI_ADDRCONFIG;
+ hints.ai_protocol = item->proto;
+ hints.ai_family = item->family;
+ if (item->family == AF_INET6) {
+ hints.ai_flags |= AI_V4MAPPED;
+ }
+
+ rv = getaddrinfo(item->name, item->serv, &hints, &results);
+ if (rv != 0) {
+ rv = nni_win_gai_errno(rv);
+ break;
+ }
+
+ // Count the total number of results.
+ aio->a_naddrs = 0;
+ for (probe = results; probe != NULL; probe = probe->ai_next) {
+ // Only count v4 and v6 addresses.
+ switch (probe->ai_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ aio->a_naddrs++;
+ break;
+ }
+ }
+ // If the only results were not IPv4 or IPv6...
+ if (aio->a_naddrs == 0) {
+ rv = NNG_EADDRINVAL;
+ break;
+ }
+ aio->a_addrs = NNI_ALLOC_STRUCTS(aio->a_addrs, aio->a_naddrs);
+ if (aio->a_addrs == NULL) {
+ aio->a_naddrs = 0;
+ rv = NNG_ENOMEM;
+ break;
+ }
+ i = 0;
+ for (probe = results; probe != NULL; probe = probe->ai_next) {
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ nng_sockaddr *sa = &aio->a_addrs[i];
+
+ switch (probe->ai_addr->sa_family) {
+ case AF_INET:
+ sin = (void *) probe->ai_addr;
+ sa->s_un.s_in.sa_family = NNG_AF_INET;
+ sa->s_un.s_in.sa_port = sin->sin_port;
+ sa->s_un.s_in.sa_addr = sin->sin_addr.s_addr;
+ i++;
+ break;
+ case AF_INET6:
+ sin6 = (void *) probe->ai_addr;
+ sa->s_un.s_in6.sa_family = NNG_AF_INET6;
+ sa->s_un.s_in6.sa_port = sin6->sin6_port;
+ memcpy(sa->s_un.s_in6.sa_addr,
+ sin6->sin6_addr.s6_addr, 16);
+ i++;
+ break;
+ default:
+ // Other address types are ignored.
+ break;
+ }
+ }
+ // Resolution complete!
+ rv = 0;
+ break;
+
+ default:
+ // Some other family requested we don't understand.
+ rv = NNG_ENOTSUP;
+ break;
+ }
+
+ if (results != NULL) {
+ freeaddrinfo(results);
+ }
+ nni_mtx_lock(&nni_win_resolv_mtx);
+ nni_win_resolv_finish(item, rv);
+ nni_mtx_unlock(&nni_win_resolv_mtx);
+}
+
+
+static void
+nni_win_resolv_ip(const char *host, const char *serv, int passive,
+ int family, int proto, nni_aio *aio)
+{
+ nni_win_resolv_item *item;
+ int rv;
+
+ if ((aio->a_naddrs != 0) && (aio->a_addrs != NULL)) {
+ NNI_FREE_STRUCTS(aio->a_addrs, aio->a_naddrs);
+ }
+ if ((item = NNI_ALLOC_STRUCT(item)) == NULL) {
+ nni_aio_finish(aio, NNG_ENOMEM, 0);
+ return;
+ }
+
+ nni_taskq_ent_init(&item->tqe, nni_win_resolv_task, item);
+
+ switch (family) {
+ case NNG_AF_INET:
+ item->family = AF_INET;
+ break;
+ case NNG_AF_INET6:
+ item->family = AF_INET6;
+ break;
+ case NNG_AF_UNSPEC:
+ item->family = AF_UNSPEC;
+ break;
+ }
+ // NB: host and serv must remain valid until this is completed.
+ item->passive = passive;
+ item->name = host;
+ item->serv = serv;
+ item->proto = proto;
+ item->aio = aio;
+
+ nni_mtx_lock(&nni_win_resolv_mtx);
+ // If we were stopped, we're done...
+ if ((rv = nni_aio_start(aio, nni_win_resolv_cancel, item)) != 0) {
+ nni_mtx_unlock(&nni_win_resolv_mtx);
+ NNI_FREE_STRUCT(item);
+ return;
+ }
+ if ((rv = nni_taskq_dispatch(nni_win_resolv_tq, &item->tqe)) != 0) {
+ nni_win_resolv_finish(item, rv);
+ nni_mtx_unlock(&nni_win_resolv_mtx);
+ return;
+ }
+ nni_mtx_unlock(&nni_win_resolv_mtx);
+}
+
+
+void
+nni_plat_tcp_resolv(const char *host, const char *serv, int family,
+ int passive, nni_aio *aio)
+{
+ nni_win_resolv_ip(host, serv, passive, family, IPPROTO_TCP, aio);
+}
+
+
+int
+nni_win_resolv_sysinit(void)
+{
+ int rv;
+
+ if ((rv = nni_mtx_init(&nni_win_resolv_mtx)) != 0) {
+ return (rv);
+ }
+ if ((rv = nni_taskq_init(&nni_win_resolv_tq, 4)) != 0) {
+ nni_mtx_fini(&nni_win_resolv_mtx);
+ return (rv);
+ }
+ return (0);
+}
+
+
+void
+nni_win_resolv_sysfini(void)
+{
+ if (nni_win_resolv_tq != NULL) {
+ nni_taskq_fini(nni_win_resolv_tq);
+ nni_win_resolv_tq = NULL;
+ }
+ nni_mtx_fini(&nni_win_resolv_mtx);
+}
+
+
+#else
+
+// Suppress empty symbols warnings in ranlib.
+int nni_win_resolv_not_used = 0;
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_thread.c b/src/platform/windows/win_thread.c
index 1b903b26..be94d638 100644
--- a/src/platform/windows/win_thread.c
+++ b/src/platform/windows/win_thread.c
@@ -159,6 +159,7 @@ nni_plat_init(int (*helper)(void))
LONG old;
static LONG initing = 0;
static LONG inited = 0;
+ int rv;
if (inited) {
return (0); // fast path
@@ -177,19 +178,28 @@ nni_plat_init(int (*helper)(void))
WORD ver;
ver = MAKEWORD(2, 2);
if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {
- InterlockedExchange(&initing, 0);
if ((LOBYTE(data.wVersion) != 2) ||
(HIBYTE(data.wVersion) != 2)) {
nni_panic("got back wrong winsock ver");
}
- return (NNG_ENOMEM);
+ rv = NNG_EINVAL;
+ goto out;
+ }
+ printf("STARTING...\n");
+ if ((rv = nni_win_iocp_sysinit()) != 0) {
+ goto out;
+ }
+ if ((rv = nni_win_resolv_sysinit()) != 0) {
+ goto out;
}
helper();
inited = 1;
}
+
+out:
InterlockedExchange(&initing, 0);
- return (0);
+ return (rv);
}
@@ -197,6 +207,8 @@ void
nni_plat_fini(void)
{
WSACleanup();
+ nni_win_resolv_sysfini();
+ nni_win_iocp_sysfini();
}