diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-01-12 20:46:20 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-01-12 21:44:44 -0800 |
| commit | 2bb6a23037656474a90d869c5147b32bae1a2e40 (patch) | |
| tree | 90697aa3fba3186296a18decb32691a1a143b50a /src/platform | |
| parent | 70348e2d4725c9ec43f51811d290c22c782058ac (diff) | |
| download | nng-2bb6a23037656474a90d869c5147b32bae1a2e40.tar.gz nng-2bb6a23037656474a90d869c5147b32bae1a2e40.tar.bz2 nng-2bb6a23037656474a90d869c5147b32bae1a2e40.zip | |
Initial swag at Win32. Much to do still.
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/windows/win_clock.c | 38 | ||||
| -rw-r--r-- | src/platform/windows/win_debug.c | 83 | ||||
| -rw-r--r-- | src/platform/windows/win_impl.h | 58 | ||||
| -rw-r--r-- | src/platform/windows/win_ipc.c | 283 | ||||
| -rw-r--r-- | src/platform/windows/win_net.c | 391 | ||||
| -rw-r--r-- | src/platform/windows/win_rand.c | 32 | ||||
| -rw-r--r-- | src/platform/windows/win_thread.c | 200 |
7 files changed, 1085 insertions, 0 deletions
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 |
