From 021d09af1375834fe9aaca916e60dcd2ad3be845 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Fri, 7 Jul 2017 18:17:51 -0700 Subject: Start of progress on Windows. Name resolution and IOCP work begins. --- src/platform/posix/posix_config.h | 4 + src/platform/posix/posix_pollq.h | 4 + src/platform/windows/win_debug.c | 49 +++++++ src/platform/windows/win_impl.h | 32 +++-- src/platform/windows/win_iocp.c | 145 +++++++++++++++++++ src/platform/windows/win_ipc.c | 129 ++++++++++------- src/platform/windows/win_net.c | 113 ++++++++++++++- src/platform/windows/win_resolv.c | 295 ++++++++++++++++++++++++++++++++++++++ src/platform/windows/win_thread.c | 18 ++- 9 files changed, 724 insertions(+), 65 deletions(-) create mode 100644 src/platform/windows/win_iocp.c create mode 100644 src/platform/windows/win_resolv.c (limited to 'src/platform') 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 +#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 +// +// 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 + +// 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 -// 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 +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 +// +// 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(); } -- cgit v1.2.3-70-g09d2