aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2017-01-12 20:46:20 -0800
committerGarrett D'Amore <garrett@damore.org>2017-01-12 21:44:44 -0800
commit2bb6a23037656474a90d869c5147b32bae1a2e40 (patch)
tree90697aa3fba3186296a18decb32691a1a143b50a
parent70348e2d4725c9ec43f51811d290c22c782058ac (diff)
downloadnng-2bb6a23037656474a90d869c5147b32bae1a2e40.tar.gz
nng-2bb6a23037656474a90d869c5147b32bae1a2e40.tar.bz2
nng-2bb6a23037656474a90d869c5147b32bae1a2e40.zip
Initial swag at Win32. Much to do still.
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/core/platform.h2
-rw-r--r--src/platform/windows/win_clock.c38
-rw-r--r--src/platform/windows/win_debug.c83
-rw-r--r--src/platform/windows/win_impl.h58
-rw-r--r--src/platform/windows/win_ipc.c283
-rw-r--r--src/platform/windows/win_net.c391
-rw-r--r--src/platform/windows/win_rand.c32
-rw-r--r--src/platform/windows/win_thread.c200
9 files changed, 1095 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c26581ed..48294b35 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -63,7 +63,6 @@ set (NNG_SOURCES
platform/posix/posix_impl.h
platform/posix/posix_config.h
-
platform/posix/posix_alloc.c
platform/posix/posix_clock.c
platform/posix/posix_debug.c
@@ -72,6 +71,14 @@ set (NNG_SOURCES
platform/posix/posix_rand.c
platform/posix/posix_thread.c
+ platform/windows/win_impl.h
+ platform/windows/win_clock.c
+ platform/windows/win_debug.c
+ platform/windows/win_ipc.c
+ platform/windows/win_net.c
+ platform/windows/win_rand.c
+ platform/windows/win_thread.c
+
protocol/bus/bus.c
protocol/pair/pair.c
diff --git a/src/core/platform.h b/src/core/platform.h
index f7bec4ca..bf8a52dc 100644
--- a/src/core/platform.h
+++ b/src/core/platform.h
@@ -256,6 +256,8 @@ extern void nni_plat_seed_prng(void *, size_t);
// get the specific types that are supplied by the platform.
#if defined(PLATFORM_POSIX)
#include "platform/posix/posix_impl.h"
+#elif defined(PLATFORM_WINDOWS)
+#include "platform/windows/win_impl.h"
#else
#error "unknown platform"
#endif
diff --git a/src/platform/windows/win_clock.c b/src/platform/windows/win_clock.c
new file mode 100644
index 00000000..3309be24
--- /dev/null
+++ b/src/platform/windows/win_clock.c
@@ -0,0 +1,38 @@
+//
+// Copyright 2016 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
+
+nni_time
+nni_plat_clock(void)
+{
+ LARGE_INTEGER freq;
+ LARGE_INTEGER count;
+ double rate;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&count);
+
+ // convert to ticks per us
+ rate = (double) freq.QuadPart / 1000000.0;
+
+ return ((nni_time) (count.QuadPart / rate));
+}
+
+
+void
+nni_plat_usleep(nni_duration usec)
+{
+ Sleep((usec + 999) / 1000);
+}
+
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_debug.c b/src/platform/windows/win_debug.c
new file mode 100644
index 00000000..189f5d4c
--- /dev/null
+++ b/src/platform/windows/win_debug.c
@@ -0,0 +1,83 @@
+//
+// 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
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void
+nni_plat_abort(void)
+{
+ abort();
+}
+
+
+void
+nni_plat_println(const char *message)
+{
+ (void) fprintf(stderr, "%s\n", message);
+}
+
+
+const char *
+nni_plat_strerror(int errnum)
+{
+ if (errnum > NNG_ESYSERR) {
+ errnum -= NNG_ESYSERR;
+ }
+ return (strerror(errnum));
+}
+
+
+#define NNI_ERR(x, y) { x, y },
+
+// Win32 has its own error codes, but these ones it shares with POSIX.
+static struct {
+ int sys_err;
+ int nng_err;
+}
+nni_plat_errnos[] = {
+ NNI_ERR(ENOENT, NNG_ENOENT)
+ NNI_ERR(EINTR, NNG_EINTR)
+ NNI_ERR(EINVAL, NNG_EINVAL)
+ NNI_ERR(ENOMEM, NNG_ENOMEM)
+ NNI_ERR(EACCES, NNG_EPERM)
+ NNI_ERR(EAGAIN, NNG_EAGAIN)
+ NNI_ERR(EBADF, NNG_ECLOSED)
+ NNI_ERR(EBUSY, NNG_EBUSY)
+ NNI_ERR(ENAMETOOLONG, NNG_EINVAL)
+ NNI_ERR(EPERM, NNG_EPERM)
+ NNI_ERR(EPIPE, NNG_ECLOSED)
+ NNI_ERR(0, 0) // must be last
+};
+
+int
+nni_plat_errno(int errnum)
+{
+ int i;
+
+ if (errnum == 0) {
+ return (0);
+ }
+ for (i = 0; nni_plat_errnos[i].nng_err != 0; i++) {
+ if (errnum == nni_plat_errnos[i].sys_err) {
+ return (nni_plat_errnos[i].nng_err);
+ }
+ }
+ // Other system errno.
+ return (NNG_ESYSERR + errnum);
+}
+
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_impl.h b/src/platform/windows/win_impl.h
new file mode 100644
index 00000000..1b306f8f
--- /dev/null
+++ b/src/platform/windows/win_impl.h
@@ -0,0 +1,58 @@
+//
+// 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.
+//
+
+#ifndef PLATFORM_WIN_IMPL_H
+#define PLATFORM_WIN_IMPL_H
+
+#ifdef PLATFORM_WINDOWS
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+#include <winsock2.h>
+#include <mswsock.h>
+#include <process.h>
+#include <ws2tcpip.h>
+
+
+// These types are provided for here, to permit them to be directly inlined
+// elsewhere.
+
+struct nni_plat_tcpsock {
+ SOCKET s;
+};
+
+struct nni_plat_ipcsock {
+ HANDLE p;
+}
+
+struct nni_plat_thr {
+ void (__stdcall *func)(void *);
+ void * arg;
+ HANDLE handle;
+};
+
+struct nni_plat_mtx {
+ CRITICAL_SECTION cs;
+ DWORD owner;
+};
+
+struct nni_plat_cv {
+ CONDITION_VARIABLE cv;
+ CRITICAL_SECTION cs;
+};
+
+#define nni_alloc(s) calloc(1, (s))
+#define nni_free(s, z) free(s)
+
+#endif // PLATFORM_WINDOWS
+
+#endif // PLATFORM_WIN_IMPL_H
diff --git a/src/platform/windows/win_ipc.c b/src/platform/windows/win_ipc.c
new file mode 100644
index 00000000..9ede99e0
--- /dev/null
+++ b/src/platform/windows/win_ipc.c
@@ -0,0 +1,283 @@
+//
+// 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
+
+
+int
+nni_plat_ipc_send(nni_plat_ipcsock *s, nni_iov *iovs, int cnt)
+{
+#if 0
+ struct iovec iov[4]; // We never have more than 3 at present
+ int i;
+ int offset;
+ int resid = 0;
+ int rv;
+
+ if (cnt > 4) {
+ return (NNG_EINVAL);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ iov[i].iov_base = iovs[i].iov_buf;
+ iov[i].iov_len = iovs[i].iov_len;
+ resid += iov[i].iov_len;
+ }
+
+ i = 0;
+ while (resid) {
+ rv = writev(s->fd, iov, cnt);
+ if (rv < 0) {
+ if (rv == EINTR) {
+ continue;
+ }
+ return (nni_plat_errno(errno));
+ }
+ if (rv > resid) {
+ nni_panic("writev says it wrote too much!");
+ }
+ resid -= rv;
+ while (rv) {
+ if (iov[i].iov_len <= rv) {
+ rv -= iov[i].iov_len;
+ i++;
+ cnt--;
+ } else {
+ iov[i].iov_len -= rv;
+ iov[i].iov_base += rv;
+ rv = 0;
+ }
+ }
+ }
+#endif
+ return (NNG_ENOTSUP);
+}
+
+
+int
+nni_plat_ipc_recv(nni_plat_ipcsock *s, nni_iov *iovs, int cnt)
+{
+#if 0
+ struct iovec iov[4]; // We never have more than 3 at present
+ int i;
+ int offset;
+ int resid = 0;
+ int rv;
+
+ if (cnt > 4) {
+ return (NNG_EINVAL);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ iov[i].iov_base = iovs[i].iov_buf;
+ iov[i].iov_len = iovs[i].iov_len;
+ resid += iov[i].iov_len;
+ }
+ i = 0;
+ while (resid) {
+ rv = readv(s->fd, iov, cnt);
+ if (rv < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ return (nni_plat_errno(errno));
+ }
+ if (rv == 0) {
+ return (NNG_ECLOSED);
+ }
+ if (rv > resid) {
+ nni_panic("readv says it read too much!");
+ }
+
+ resid -= rv;
+ while (rv) {
+ if (iov[i].iov_len <= rv) {
+ rv -= iov[i].iov_len;
+ i++;
+ cnt--;
+ } else {
+ iov[i].iov_len -= rv;
+ iov[i].iov_base += rv;
+ rv = 0;
+ }
+ }
+ }
+#endif
+ return (NNG_ENOTSUP);
+}
+
+
+void
+nni_plat_ipc_init(nni_plat_ipcsock *s)
+{
+ s->p = INVALID_HANDLE_VALUE;
+}
+
+
+void
+nni_plat_ipc_fini(nni_plat_ipcsock *s)
+{
+ if (s->p != INVALID_HANDLE_VALUE) {
+ (void) CloseHandle(s->fd);
+ s->p = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+void
+nni_plat_ipc_shutdown(nni_plat_ipcsock *s)
+{
+ if (s->p != INVALID_HANDLE_VALUE) {
+#if 0
+ (void) shutdown(s->fd, SHUT_RDWR);
+ // This causes the equivalent of a close. Hopefully waking
+ // up anything that didn't get the hint with the shutdown.
+ // (macOS does not see the shtudown).
+ (void) dup2(nni_plat_devnull, s->fd);
+#endif
+ }
+}
+
+
+// nni_plat_ipc_listen creates a file descriptor bound to the given address.
+// This basically does the equivalent of socket, bind, and listen. We have
+// chosen a default value for the listen backlog of 128, which should be
+// plenty. (If it isn't, then the accept thread can't get enough resources
+// to keep up, and your clients are going to experience bad things. Normally
+// the actual backlog should hover near 0 anyway.)
+int
+nni_plat_ipc_listen(nni_plat_ipcsock *s, const char *path)
+{
+#if 0
+ int fd, checkfd;
+ struct sockaddr_un sun;
+ int rv;
+
+ if (nni_plat_ipc_path_to_sockaddr(&sun, path) < 0) {
+ return (NNG_EADDRINVAL);
+ }
+
+ if ((fd = socket(AF_UNIX, NNI_IPC_SOCKTYPE, 0)) < 0) {
+ return (nni_plat_errno(errno));
+ }
+
+ // We are going to check to see if there was a name already there.
+ // If there was, and nothing is listening (ECONNREFUSED), then we
+ // will just try to cleanup the old socket. Note that this is not
+ // perfect in all scenarios, so use this with caution.
+ if ((checkfd = socket(AF_UNIX, NNI_IPC_SOCKTYPE, 0)) < 0) {
+ (void) close(fd);
+ return (nni_plat_errno(errno));
+ }
+
+ // Nonblocking because we don't want to wait for any remote server.
+ (void) fcntl(checkfd, F_SETFL, O_NONBLOCK);
+ if (connect(checkfd, (struct sockaddr *) &sun, sizeof (sun)) < 0) {
+ if (errno == ECONNREFUSED) {
+ (void) unlink(path);
+ }
+ }
+ (void) close(checkfd);
+
+ nni_plat_ipc_setopts(fd);
+
+ if ((s->unlink = nni_alloc(strlen(path) + 1)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ strcpy(s->unlink, path);
+ if (bind(fd, (struct sockaddr *) &sun, sizeof (sun)) < 0) {
+ rv = nni_plat_errno(errno);
+ nni_free(s->unlink, strlen(path) + 1);
+ s->unlink = NULL;
+ (void) close(fd);
+ return (rv);
+ }
+
+ // Listen -- 128 depth is probably sufficient. If it isn't, other
+ // bad things are going to happen.
+ if (listen(fd, 128) < 0) {
+ rv = nni_plat_errno(errno);
+ (void) close(fd);
+ return (rv);
+ }
+ s->fd = fd;
+#endif
+ return (NNG_ENOTSUP);
+}
+
+
+int
+nni_plat_ipc_connect(nni_plat_ipcsock *s, const char *path)
+{
+#if 0
+ int fd;
+ int len;
+ struct sockaddr_un sun;
+ int rv;
+
+ if (nni_plat_ipc_path_to_sockaddr(&sun, path) < 0) {
+ return (NNG_EADDRINVAL);
+ }
+
+ if ((fd = socket(AF_UNIX, NNI_IPC_SOCKTYPE, 0)) < 0) {
+ return (nni_plat_errno(errno));
+ }
+
+ nni_plat_ipc_setopts(fd);
+
+ if (connect(fd, (struct sockaddr *) &sun, sizeof (sun)) != 0) {
+ rv = nni_plat_errno(errno);
+ (void) close(fd);
+ if (rv == NNG_ENOENT) {
+ // In this case we want to treat this the same as
+ // ECONNREFUSED, since they mean the same to us.
+ rv = NNG_ECONNREFUSED;
+ }
+ return (rv);
+ }
+ s->fd = fd;
+#endif
+ return (NNG_ENOTSUP);
+}
+
+
+int
+nni_plat_ipc_accept(nni_plat_ipcsock *s, nni_plat_ipcsock *server)
+{
+#if 0
+ int fd;
+
+ for (;;) {
+ fd = accept(server->fd, NULL, NULL);
+
+ if (fd < 0) {
+ if ((errno == EINTR) || (errno == ECONNABORTED)) {
+ // These are not fatal errors, keep trying
+ continue;
+ }
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+ continue;
+ }
+ return (nni_plat_errno(errno));
+ } else {
+ break;
+ }
+ }
+
+ nni_plat_ipc_setopts(fd);
+
+ s->fd = fd;
+#endif
+ return (NNG_ENOTSUP);
+}
+
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_net.c b/src/platform/windows/win_net.c
new file mode 100644
index 00000000..7c44c125
--- /dev/null
+++ b/src/platform/windows/win_net.c
@@ -0,0 +1,391 @@
+//
+// 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
+
+static struct {
+ int wsa_err;
+ int nng_err;
+}
+nni_plat_wsa_errnos[] = {
+ { WSAECONNABORTED, NNG_ECONNABORTED },
+ { WSAEINTR, NNG_EINTR },
+ { WSAEFAULT, NNG_EFAULT },
+ { WSAECONNRESET, NNG_ECONNRESET },
+ { WSAEMSGSIZE, NNG_EINVAL },
+ { WSAENETDOWN, NNG_EUNREACHABLE },
+ { WSAENETRESET, NNG_ECLOSED },
+ { WSAENOBUFS, NNG_ENOMEM },
+ { WSAESHUTDOWN, NNG_ECLOSED },
+ { WSAEWOULDBLOCK, NNG_EAGAIN },
+ { WSAEBADF, NNG_ECLOSED },
+ { WSA_INVALID_HANDLE, NNG_ECLOSED },
+ { WSA_NOT_ENOUGH_MEMORY, NNG_ENOMEM },
+ { WSA_INVALID_PARAMETER, NNG_EINVAL },
+ { WSAEACCESS, NNG_EPERM },
+ { 0, 0 }, // MUST BE LAST
+};
+
+
+static int
+nni_plat_wsa_last_error(void)
+{
+ errnum = WSAGetLastError();
+ if (errnum == 0) {
+ return (0);
+ }
+ for (i = 0; nni_plat_wsa_errnos[i].nng_err != 0; i++) {
+ if (errnum == nni_plat_wsa_errnos[i].wsa_err) {
+ return (nni_plat_errnos[i].nng_err);
+ }
+ }
+ // Other system errno.
+ return (NNG_ESYSERR + errnum);
+}
+
+
+static int
+nni_plat_to_sockaddr(struct SOCKADDR_STORAGE *ss, const nni_sockaddr *sa)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ switch (sa->s_un.s_family) {
+ case NNG_AF_INET:
+ sin = (void *) ss;
+ memset(sin, 0, sizeof (*sin));
+ sin->sin_family = PF_INET;
+ sin->sin_port = sa->s_un.s_in.sa_port;
+ sin->sin_addr.s_addr = sa->s_un.s_in.sa_addr;
+ return (sizeof (*sin));
+
+ case NNG_AF_INET6:
+ sin6 = (void *) ss;
+ memset(&sin6, 0, sizeof (sin6));
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_port = sa->s_un.s_in6.sa_port;
+ memcpy(sin6->sin6_addr.s6_addr, sa->s_un.s_in6.sa_addr, 16);
+ return (sizeof (*sin6));
+ }
+ return (-1);
+}
+
+
+static int
+nni_plat_from_sockaddr(nni_sockaddr *sa, const struct sockaddr *ss)
+{
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+
+ memset(sa, 0, sizeof (*sa));
+ switch (ss->sa_family) {
+ case PF_INET:
+ sin = (const void *) ss;
+ 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;
+ return (0);
+
+ case PF_INET6:
+ sin6 = (const void *) ss;
+ 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);
+ return (0);
+ }
+ return (-1);
+}
+
+
+int
+nni_plat_lookup_host(const char *host, nni_sockaddr *addr, int flags)
+{
+ ADDRINFO hint;
+ ADDIRINFO *ai;
+
+ memset(&hint, 0, sizeof (hint));
+ hint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hint.ai_family = PF_UNSPEC;
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+ if (flags & NNI_FLAG_IPV4ONLY) {
+ hint.ai_family = PF_INET;
+ }
+
+ if (getaddrinfo(host, NULL, &hint, &ai) != 0) {
+ return (NNG_EADDRINVAL);
+ }
+
+ if (nni_plat_from_sockaddr(addr, ai->ai_addr) < 0) {
+ freeaddrinfo(ai);
+ return (NNG_EADDRINVAL);
+ }
+ freeaddrinfo(ai);
+ return (0);
+}
+
+
+int
+nni_plat_tcp_send(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
+{
+ WSABUF iov[4]; // We never have more than 3 at present
+ int i;
+ DWORD sent = 0;
+ int rv;
+
+ if (cnt > 4) {
+ return (NNG_EINVAL);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ iov[i].buf = iovs[i].iov_buf;
+ iov[i].len = iovs[i].iov_len;
+ }
+
+ rv = WSASend(s->s, iov, cnt, &sent, 0, NULL, NULL);
+ if (rv != 0) {
+ // XXX: CONVERT WSAGetLastError code.
+ return (nni_plat_wsa_last_error());
+ }
+
+ return (0);
+}
+
+
+int
+nni_plat_tcp_recv(nni_plat_tcpsock *s, nni_iov *iovs, int cnt)
+{
+ WSABUF iov[4]; // We never have more than 3 at present
+ int i;
+ int offset;
+ int resid = 0;
+ int rv;
+ DWORD nrecv;
+
+ if (cnt > 4) {
+ return (NNG_EINVAL);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ iov[i].buf = iovs[i].iov_buf;
+ iov[i].len = iovs[i].iov_len;
+ resid += iov[i].len;
+ }
+
+ i = 0;
+ while (resid) {
+ rv = WSARecv(s->s, iov, cnt, &nrecv, 0, NULL, NULL);
+ if (rv != 0) {
+ return (nni_plat_wsa_last_error());
+ }
+ if (nrecv > resid) {
+ nni_panic("readv says it read too much!");
+ }
+
+ resid -= nrecv;
+ while (nrecv) {
+ if (iov[i].len <= nrecv) {
+ nrecv -= iov[i].len;
+ i++;
+ cnt--;
+ } else {
+ iov[i].len -= nrecv;
+ iov[i].buf += nrecv;
+ nrecv = 0;
+ }
+ }
+ }
+
+ return (0);
+}
+
+
+static void
+nni_plat_tcp_setopts(int fd)
+{
+ int one;
+
+ // Don't inherit the handle (CLOEXEC really).
+ SetHandleInformation(s->s, HANDLE_FLAG_INHERIT, 0);
+
+ // Also disable Nagle. We are careful to group data with WSASend,
+ // and latency is king for most of our users. (Consider adding
+ // a method to enable this later.)
+ one = 1;
+ (void) setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof (one));
+}
+
+
+void
+nni_plat_tcp_init(nni_plat_tcpsock *s)
+{
+ s->s = INVALID_SOCKET;
+}
+
+
+void
+nni_plat_tcp_fini(nni_plat_tcpsock *s)
+{
+ if (s->s != INVALID_SOCKET) {
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ }
+}
+
+
+void
+nni_plat_tcp_shutdown(nni_plat_tcpsock *s)
+{
+ if (s->s != INVALID_SOCKET) {
+ (void) shutdown(s->fd, SHUT_RDWR);
+ }
+}
+
+
+// nni_plat_tcp_listen creates a file descriptor bound to the given address.
+// This basically does the equivalent of socket, bind, and listen. We have
+// chosen a default value for the listen backlog of 128, which should be
+// plenty. (If it isn't, then the accept thread can't get enough resources
+// to keep up, and your clients are going to experience bad things. Normally
+// the actual backlog should hover near 0 anyway.)
+int
+nni_plat_tcp_listen(nni_plat_tcpsock *s, const nni_sockaddr *addr)
+{
+ int len;
+ struct sockaddr_storage ss;
+ int rv;
+ ULONG one;
+
+ len = nni_plat_to_sockaddr(&ss, addr);
+ if (len < 0) {
+ return (NNG_EADDRINVAL);
+ }
+
+ s->s = WSASocket(ss.ss_family, SOCK_STREAM, 0, NULL, 0,
+ WSA_FLAG_NO_HANDLE_INHERIT);
+ if (s->s == INVALID_SOCKET) {
+ return (nni_plat_wsa_last_error());
+ }
+
+ nni_plat_tcp_setopts(s->s);
+
+ // Make sure that we use the address exclusively. Windows lets
+ // others hijack us by default.
+ one = 1;
+ if (setsocket(s->s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &one,
+ sizeof (one)) == SOCKET_ERROR) {
+ rv = nni_plat_wsa_last_error();
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (rv);
+ }
+ if (bind(s->s, (struct sockaddr *) &ss, len) != 0) {
+ rv = nni_plat_wsa_last_error();
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (rv);
+ }
+
+ // Listen -- 128 depth is probably sufficient. If it isn't, other
+ // bad things are going to happen.
+ if (listen(s->s, 128) != 0) {
+ rv = nni_plat_wsa_last_error();
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (rv);
+ }
+
+ return (0);
+}
+
+
+// nni_plat_tcp_connect establishes an outbound connection. It the
+// bind address is not null, then it will attempt to bind to the local
+// address specified first.
+int
+nni_plat_tcp_connect(nni_plat_tcpsock *s, const nni_sockaddr *addr,
+ const nni_sockaddr *bindaddr)
+{
+ int len;
+ struct sockaddr_storage ss;
+ struct sockaddr_storage bss;
+ int rv;
+
+ len = nni_plat_to_sockaddr(&ss, addr);
+ if (len < 0) {
+ return (NNG_EADDRINVAL);
+ }
+
+ s->s = WSASocket(ss.ss_family, SOCK_STREAM, 0, NULL, 0,
+ WSA_FLAG_NO_HANDLE_INHERIT);
+ if (s->s == INVALID_SOCKET) {
+ return (nni_plat_wsa_last_error());
+ }
+
+ if (bindaddr != NULL) {
+ if (bindaddr->s_un.s_family != addr->s_un.s_family) {
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (NNG_EINVAL);
+ }
+ if (nni_plat_to_sockaddr(&bss, bindaddr) < 0) {
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (NNG_EADDRINVAL);
+ }
+ if (bind(s->s, (struct sockaddr *) &bss, len) < 0) {
+ rv = nni_plat_wsa_last_error(errno);
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (rv);
+ }
+ }
+
+ nni_plat_tcp_setopts(s->s);
+
+ if (connect(s->s, (struct sockaddr *) &ss, len) != 0) {
+ rv = nni_plat_wsa_last_error();
+ (void) closesocket(s->s);
+ s->s = INVALID_SOCKET;
+ return (rv);
+ }
+ return (0);
+}
+
+
+int
+nni_plat_tcp_accept(nni_plat_tcpsock *s, nni_plat_tcpsock *server)
+{
+ SOCKET fd;
+ int err;
+
+ for (;;) {
+ fd = accept(server->s, NULL, NULL);
+
+ if (fd == INVALID_SOCKET) {
+ err = WSAGetLastError();
+ if (err == WSAECONNRESET || err == WSAEWOULDBLOCK) {
+ continue;
+ }
+ return (nni_plat_wsa_last_error());
+ } else {
+ break;
+ }
+ }
+
+ nni_plat_tcp_setopts(fd);
+
+ s->s = fd;
+ return (0);
+}
+
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_rand.c b/src/platform/windows/win_rand.c
new file mode 100644
index 00000000..5ca80782
--- /dev/null
+++ b/src/platform/windows/win_rand.c
@@ -0,0 +1,32 @@
+//
+// 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
+
+#include <stdlib.h>
+
+void
+nni_plat_seed_prng(void *buf, size_t bufsz)
+{
+ unsigned val;
+ // The rand_s routine uses RtlGenRandom to get high quality
+ // pseudo random numbers (i.e. numbers that should be good enough
+ // for use with crypto keying.)
+ while (bufsz > sizeof (val)) {
+ rand_s(&val);
+ memcmp(buf, &val);
+ buf = (((char *)buf) + sizeof (val));
+ bufsz -= sizeof (val);
+ }
+}
+
+
+#endif // PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_thread.c b/src/platform/windows/win_thread.c
new file mode 100644
index 00000000..2c967b84
--- /dev/null
+++ b/src/platform/windows/win_thread.c
@@ -0,0 +1,200 @@
+//
+// 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.
+//
+
+// POSIX threads.
+
+#include "core/nng_impl.h"
+
+#ifdef PLATFORM_WINDOWS
+
+int
+nni_plat_mtx_init(nni_plat_mtx *mtx)
+{
+ InitializeCritialSection(&mtx->cs);
+ mtx->owner = 0;
+ return (0);
+}
+
+
+void
+nni_plat_mtx_fini(nni_plat_mtx *mtx)
+{
+ if (mtx->owner != 0) {
+ nni_panic("cannot delete critical section: mutex owned!")
+ }
+ DeleteCriticalSection(&mtx->cs);
+}
+
+
+void
+nni_plat_mtx_lock(nni_plat_mtx *mtx)
+{
+ EnterCriticalSection(&mtx->cs);
+ if (mtx->owner != 0) {
+ nni_panic("recursive mutex entry!");
+ }
+ mtx->owner = GetCurrentThreadId();
+}
+
+
+void
+nni_plat_mtx_unlock(nni_plat_mtx *mtx)
+{
+ if (mtx->owner != GetCurrentThreadId()) {
+ nni_panic("cannot unlock mutex: not owner!");
+ }
+ self->owner = 0;
+ LeaveCriticalSection(&mtx->cs);
+}
+
+
+int
+nni_plat_mtx_trylock(nni_plat_mtx *mtx)
+{
+ BOOL ok;
+
+ ok = TryEnterCriticalSection(&mtx->cs);
+ if (!ok) {
+ return (NNG_EBUSY);
+ }
+ if (mtx->owner != 0) {
+ nni_panic("recursive trymutex entry?!?")
+ }
+ mtx->owner = GetCurrentThreadId();
+ return (0);
+}
+
+
+int
+nni_plat_cv_init(nni_plat_cv *cv, nni_plat_mtx *mtx)
+{
+ InitializeConditionVariable(&cv->cv);
+ cv->cs = &mtx->cs;
+ return (0);
+}
+
+
+void
+nni_plat_cv_wake(nni_plat_cv *cv)
+{
+ int rv;
+
+ if ((rv = pthread_cond_broadcast(&cv->cv)) != 0) {
+ nni_panic("pthread_cond_broadcast: %s", strerror(rv));
+ }
+}
+
+
+void
+nni_plat_cv_wait(nni_plat_cv *cv)
+{
+ (void) SleepConditionVariable(&cv->cv, &cv->cs, INFINITE);
+}
+
+
+int
+nni_plat_cv_until(nni_plat_cv *cv, nni_time until)
+{
+ nni_time now;
+ DWORD msec;
+ BOOL ok;
+
+ now = nni_plat_clock();
+ if (now > until) {
+ msec = 0;
+ } else {
+ // times are in usec, but win32 wants millis
+ msec = (until - now)/1000;
+ }
+
+ ok = SleepConditionVariable(&cv->cv, &cv->cs, msec);
+ return (ok ? 0 : NNG_ETIMEDOUT);
+}
+
+
+void
+nni_plat_cv_fini(nni_plat_cv *cv)
+{
+}
+
+
+static unsigned int __stdcall
+nni_plat_thread_main(void *arg)
+{
+ nni_plat_thr *thr = arg;
+
+ thr->func(thr->arg);
+ return (0);
+}
+
+
+int
+nni_plat_thr_init(nni_plat_thr *thr, void (*fn)(void *), void *arg)
+{
+ thr->func = fn;
+ thr->arg = arg;
+
+ thr->handle = (HANDLE) _beginthreadex(NULL, 0,
+ nni_plat_thr_main, thr, 0, NULL);
+ if (thr->handle == NULL) {
+ return (NNG_ENOMEM); // Best guess...
+ }
+ return (0);
+}
+
+
+void
+nni_plat_thr_fini(nni_plat_thr *thr)
+{
+ if (WaitForSingleObject(thr->handle, INFINITE) == WAIT_FAILED) {
+ nni_panic("waiting for thread failed!");
+ }
+ if (CloseHandle(thr->handle) == 0) {
+ nni_panic("close handle for thread failed!");
+ }
+}
+
+
+int
+nni_plat_init(int (*helper)(void))
+{
+ int rv;
+ LONG old;
+ static LONG initing = 0;
+ static LONG inited = 0;
+
+ if (inited) {
+ return (0); // fast path
+ }
+
+ // This logic gets us to initialize the platform just once.
+ // If two threads enter here together, only one will get to run,
+ // and the other will be put to sleep briefly so that the first
+ // can complete. This is a poor man's singleton initializer, since
+ // we can't statically initialize critical sections.
+ while ((old = InterlockedTestExchange(&initing, 0, 1)) != 0) {
+ Sleep(1);
+ }
+ if (!inited) {
+ helper();
+ inited = 1;
+ }
+ InterlockExchange(&initing, 0);
+
+ return (rv);
+}
+
+
+void
+nni_plat_fini(void)
+{
+}
+
+
+#endif