windows: migrate to WSAPoll

Switch out the guts of the default window platform
wait to use WSAPoll(), switch the lws_cancel_service()
mechanism to use a UDP socket pair.
diff --git a/include/libwebsockets.h b/include/libwebsockets.h
index c79d4e6..18cbe61 100644
--- a/include/libwebsockets.h
+++ b/include/libwebsockets.h
@@ -369,15 +369,11 @@
 typedef HANDLE lws_filefd_type;
 #endif
 
-struct lws_pollfd {
-	lws_sockfd_type fd; /**< file descriptor */
-	SHORT events; /**< which events to respond to */
-	SHORT revents; /**< which events happened */
-	uint8_t write_blocked;
-};
-#define LWS_POLLHUP (FD_CLOSE)
-#define LWS_POLLIN (FD_READ | FD_ACCEPT)
-#define LWS_POLLOUT (FD_WRITE)
+
+#define lws_pollfd pollfd
+#define LWS_POLLHUP	(POLLHUP)
+#define LWS_POLLIN	(POLLRDNORM | POLLRDBAND)
+#define LWS_POLLOUT	(POLLWRNORM)
 
 #else
 
diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c
index 4411438..994aa84 100644
--- a/lib/core-net/client/connect3.c
+++ b/lib/core-net/client/connect3.c
@@ -56,41 +56,57 @@
 static lcccr_t
 lws_client_connect_check(struct lws *wsi)
 {
-#if !defined(WIN32)
-	socklen_t sl = sizeof(int);
 	int e = 0;
+	int en = 0;
+	socklen_t sl = sizeof(e);
 
 	/*
 	 * This resets SO_ERROR after reading it.  If there's an error
 	 * condition, the connect definitively failed.
 	 */
 
-	if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, &e, &sl)) {
+	if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
+#if defined(WIN32)
+			(char *)
+#endif
+			&e, &sl)) {
+		en = LWS_ERRNO;
 		if (!e) {
 			lwsl_debug("%s: getsockopt check: conn OK errno %d\n",
-				   __func__, errno);
+				   __func__, en);
 
 			return LCCCR_CONNECTED;
 		}
 
-		lwsl_debug("%s: getsockopt fd %d says err %d\n", __func__,
+		lwsl_notice("%s: getsockopt fd %d says err %d\n", __func__,
 			   wsi->desc.sockfd, e);
 	}
 
-#else
+#if defined(WIN32)
+
 	if (!connect(wsi->desc.sockfd, NULL, 0))
 		return LCCCR_CONNECTED;
 
-	if (!LWS_ERRNO || LWS_ERRNO == WSAEINVAL ||
-			  LWS_ERRNO == WSAEWOULDBLOCK ||
-			  LWS_ERRNO == WSAEALREADY) {
-		lwsl_info("%s: errno %d\n", __func__, errno);
+	en = LWS_ERRNO;
 
+	if (en == WSAEISCONN) /* already connected */
+		return LCCCR_CONNECTED;
+
+	if (en == WSAEALREADY) {
+		/* reset the POLLOUT wait */
+		if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
+			lwsl_notice("pollfd failed\n");
+	}
+
+	if (!en || en == WSAEINVAL ||
+		   en == WSAEWOULDBLOCK ||
+		   en == WSAEALREADY) {
+		lwsl_debug("%s: errno %d\n", __func__, en);
 		return LCCCR_CONTINUE;
 	}
 #endif
 
-	lwsl_info("%s: connect check take as FAILED\n", __func__);
+	lwsl_notice("%s: connect check take as FAILED: errno %d\n", __func__, en);
 
 	return LCCCR_FAILED;
 }
@@ -137,9 +153,6 @@
 		result = NULL;
 	}
 
-	if (n == LWS_CONNECT_COMPLETION_GOOD)
-               goto conn_good;
-
 #if defined(LWS_WITH_IPV6) && defined(__ANDROID__)
 	ipv6only = 0;
 #endif
@@ -455,14 +468,12 @@
 				 wsi->a.context->timeout_secs *
 						 LWS_USEC_PER_SEC);
 
-#if !defined(WIN32)
 		/*
 		 * must do specifically a POLLOUT poll to hear
 		 * about the connect completion
 		 */
 		if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
 			goto try_next_dns_result_fds;
-#endif
 
 		return wsi;
 	}
diff --git a/lib/core-net/pollfd.c b/lib/core-net/pollfd.c
index af9fe40..d31f9aa 100644
--- a/lib/core-net/pollfd.c
+++ b/lib/core-net/pollfd.c
@@ -138,8 +138,10 @@
 	lws_memory_barrier();
 #endif
 
-#if !defined(__linux__)
-	/* OSX couldn't see close on stdin pipe side otherwise */
+#if !defined(__linux__) && !defined(WIN32)
+	/* OSX couldn't see close on stdin pipe side otherwise; WSAPOLL
+	 * blows up if we give it POLLHUP
+	 */
 	_or |= LWS_POLLHUP;
 #endif
 
@@ -190,6 +192,7 @@
 	 *         then cancel it to force a restart with our changed events
 	 */
 	pa_events = pa->prev_events != pa->events;
+	pfd->events = (short)pa->events;
 
 	if (pa_events) {
 		if (lws_plat_change_pollfd(context, wsi, pfd)) {
diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h
index d3db071..65c425a 100644
--- a/lib/core-net/private-lib-core-net.h
+++ b/lib/core-net/private-lib-core-net.h
@@ -392,10 +392,7 @@
 
 	struct lws_pollfd *fds;
 	volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
-#ifdef _WIN32
-       WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];
-	CRITICAL_SECTION interrupt_lock;
-#endif
+
 	lws_sockfd_type dummy_pipe_fds[2];
 	struct lws *pipe_wsi;
 
diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c
index c665968..7b25153 100644
--- a/lib/core-net/vhost.c
+++ b/lib/core-net/vhost.c
@@ -1510,7 +1510,7 @@
 		    !(newconn_cannot_use_h1 && w->role_ops != &role_ops_h1) &&
 		    !strcmp(adsin, w->cli_hostname_copy) &&
 #if defined(LWS_WITH_TLS)
-		    (!(wsi->tls.use_ssl & LCCSCF_USE_SSL) || !my_alpn || (my_alpn && strstr(my_alpn, "http/1.1"))) &&
+		   (!(wsi->tls.use_ssl & LCCSCF_USE_SSL) || !my_alpn || (my_alpn && strstr(my_alpn, "http/1.1"))) &&
 		    (wsi->tls.use_ssl & LCCSCF_USE_SSL) ==
 		     (w->tls.use_ssl & LCCSCF_USE_SSL) &&
 #endif
diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h
index 81ceb3a..eb0668d 100644
--- a/lib/core/private-lib-core.h
+++ b/lib/core/private-lib-core.h
@@ -410,7 +410,7 @@
 	lws_sorted_usec_list_t			sul_nl_coldplug;
 #endif
 
-#if defined(LWS_PLAT_FREERTOS)
+#if defined(LWS_PLAT_FREERTOS) || defined(WIN32)
 	struct sockaddr_in			frt_pipe_si;
 #endif
 
diff --git a/lib/plat/windows/windows-init.c b/lib/plat/windows/windows-init.c
index 36778b6..f60c8bf 100644
--- a/lib/plat/windows/windows-init.c
+++ b/lib/plat/windows/windows-init.c
@@ -87,11 +87,7 @@
 	}
 
 	while (n--) {
-               int m;
 		pt->fds_count = 0;
-               for (m = 0; m < WSA_MAXIMUM_WAIT_EVENTS; m++)
-                       pt->events[m] = WSACreateEvent();
-		InitializeCriticalSection(&pt->interrupt_lock);
 
 		pt++;
 	}
@@ -111,16 +107,7 @@
 void
 lws_plat_context_early_destroy(struct lws_context *context)
 {
-	struct lws_context_per_thread *pt = &context->pt[0];
-	int n = context->count_threads;
 
-	while (n--) {
-		int m;
-		for (m = 0; m < WSA_MAXIMUM_WAIT_EVENTS; m++)
-			WSACloseEvent(pt->events[m]);
-		DeleteCriticalSection(&pt->interrupt_lock);
-		pt++;
-	}
 }
 
 void
diff --git a/lib/plat/windows/windows-pipe.c b/lib/plat/windows/windows-pipe.c
index 3b8d999..5dd2bf6 100644
--- a/lib/plat/windows/windows-pipe.c
+++ b/lib/plat/windows/windows-pipe.c
@@ -27,9 +27,69 @@
 #endif
 #include "private-lib-core.h"
 
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+
 int
 lws_plat_pipe_create(struct lws *wsi)
 {
+	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
+	struct sockaddr_in *si = &wsi->a.context->frt_pipe_si;
+	lws_sockfd_type *fd = pt->dummy_pipe_fds;
+	socklen_t sl;
+
+	/*
+	 * Non-WSA HANDLEs can't join the WSAPoll() wait... use a UDP socket
+	 * listening on 127.0.0.1:xxxx and send a byte to it from a second UDP
+	 * socket to cancel the wait.
+	 *
+	 * Set the port to 0 at the bind, so lwip will choose a free one in the
+	 * ephemeral range for us.
+	 */
+
+	fd[0] = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd[0] == INVALID_SOCKET)
+		goto bail;
+
+	fd[1] = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd[1] == INVALID_SOCKET)
+		goto bail;
+
+	/*
+	 * No need for memset since it's in zalloc'd context... it's in the
+	 * context so we can reuse the prepared sockaddr to send tp fd[0] whem
+	 * we want to cancel the wait
+	 */
+
+	si->sin_family = AF_INET;
+	si->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	si->sin_port = 0;
+
+	if (bind(fd[0], (const struct sockaddr *)si, sizeof(*si)) < 0)
+		goto bail;
+
+	/*
+	 * Query the socket to set context->frt_pipe_si to the full sockaddr it
+	 * wants to be addressed by, including the port that lwip chose.
+	 *
+	 * Afterwards, we can use this prepared sockaddr stashed in the context
+	 * to trigger the "pipe" without any other preliminaries.
+	 */
+
+	sl = sizeof(*si);
+	if (getsockname(fd[0], (struct sockaddr *)si, &sl))
+		goto bail;
+
+	lwsl_info("%s: cancel UDP skt port %d\n", __func__,
+		  ntohs(si->sin_port));
+
+	return 0;
+
+bail:
+	lwsl_err("%s: failed\n", __func__);
+
 	return 1;
 }
 
@@ -37,21 +97,31 @@
 lws_plat_pipe_signal(struct lws_context *ctx, int tsi)
 {
 	struct lws_context_per_thread *pt = &ctx->pt[tsi];
+	struct sockaddr_in *si = &ctx->frt_pipe_si;
+	lws_sockfd_type *fd = pt->dummy_pipe_fds;
+	char u = 0;
+	int n;
 
 	/*
-	 * We need the critical section so that we are either setting it or
-	 * clearing it, no matter how many threads competing there is a clear
-	 * atomic state for the event
+	 * Send a single UDP byte payload to the listening socket fd[0], forcing
+	 * the event loop wait to wake.  fd[1] and context->frt_pipe_si are
+	 * set at context creation and are static.
 	 */
 
-	EnterCriticalSection(&pt->interrupt_lock);
-	WSASetEvent(pt->events[0]); /* trigger the cancel event */
-	LeaveCriticalSection(&pt->interrupt_lock);
+	n = sendto(fd[1], &u, 1, 0, (struct sockaddr *)si, sizeof(*si));
 
-	return 0;
+	return n != 1;
 }
 
 void
 lws_plat_pipe_close(struct lws *wsi)
 {
+	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
+
+	if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != LWS_SOCK_INVALID)
+		closesocket(pt->dummy_pipe_fds[0]);
+	if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != LWS_SOCK_INVALID)
+		closesocket(pt->dummy_pipe_fds[1]);
+
+	pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = LWS_SOCK_INVALID;
 }
diff --git a/lib/plat/windows/windows-service.c b/lib/plat/windows/windows-service.c
index daeaf3c..4cc94e5 100644
--- a/lib/plat/windows/windows-service.c
+++ b/lib/plat/windows/windows-service.c
@@ -60,12 +60,10 @@
 _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 {
 	struct lws_context_per_thread *pt;
-	WSANETWORKEVENTS networkevents;
 	struct lws_pollfd *pfd;
 	lws_usec_t timeout_us;
 	struct lws *wsi;
 	unsigned int i;
-	DWORD ev;
 	int n;
 
 	/* stay dead once we are dead */
@@ -150,69 +148,15 @@
 	if (!lws_service_adjust_timeout(context, 1, tsi))
 		timeout_us = 0;
 
-	/*
-	 * WSA cannot actually tell us this from the wait... if anyone wants
-	 * POLLOUT and is not blocked for it, no need to wait since we will want
-	 * to service at least those.   Still enter the wait so we can pick up
-	 * other pending things...
-	 */
-
-	for (n = 0; n < (int)pt->fds_count; n++)
-		if (pt->fds[n].fd != LWS_SOCK_INVALID &&
-		    pt->fds[n].events & LWS_POLLOUT &&
-		    !pt->fds[n].write_blocked) {
-			timeout_us = 0;
-			break;
-		}
-
-	// lwsl_notice("%s: to %dms\n", __func__, (int)(timeout_us / 1000));
-	ev = WSAWaitForMultipleEvents(pt->fds_count + 1, pt->events, FALSE,
-				      (DWORD)(timeout_us / LWS_US_PER_MS),
-				      FALSE);
-	//lwsl_notice("%s: ev 0x%x\n", __func__, ev);
-
-	/*
-	 * The wait returns indicating the one event that had something, or
-	 * that we timed out, or something broken.
-	 *
-	 * Amazingly WSA can only handle 64 events, because the errors start
-	 * at ordinal 64.
-	 */
-
-	if (ev >= WSA_MAXIMUM_WAIT_EVENTS &&
-	    ev != WSA_WAIT_TIMEOUT)
-		/* some kind of error */
-		return 0;
-
-       if (!ev) {
-	       /*
-	        * The zero'th event is the cancel event specifically.  Lock
-	        * the event reset so we are definitely clearing it while we
-	        * try to clear it.
-	        */
-		EnterCriticalSection(&pt->interrupt_lock);
-		WSAResetEvent(pt->events[0]);
-		LeaveCriticalSection(&pt->interrupt_lock);
-
-#if defined(LWS_WITH_THREADPOOL)
-		/*
-		 * threadpools that need to call for on_writable callbacks do it by
-		 * marking the task as needing one for its wsi, then cancelling service.
-		 *
-		 * Each tsi will call this to perform the actual callback_on_writable
-		 * from the correct service thread context
-		 */
-		lws_threadpool_tsi_context(pt->context, pt->tid);
-#endif
-
-		lws_broadcast(pt, LWS_CALLBACK_EVENT_WAIT_CANCELLED, NULL, 0);
-
+//	lwsl_notice("%s: in %dms, count %d\n", __func__, (int)(timeout_us / 1000), pt->fds_count);
+//	for (n = 0; n < (int)pt->fds_count; n++)
+//		lwsl_notice("%s: fd %d ev 0x%x POLLIN %d, POLLOUT %d\n", __func__, (int)pt->fds[n].fd, (int)pt->fds[n].events, POLLIN, POLLOUT);
+	int d = WSAPoll((WSAPOLLFD *)&pt->fds[0], pt->fds_count, (int)(timeout_us / LWS_US_PER_MS));
+	if (d < 0) {
+		lwsl_err("%s: WSAPoll failed: count %d, err %d: %d\n", __func__, pt->fds_count, d, WSAGetLastError());
 		return 0;
 	}
-
-       /*
-        * Otherwise at least fds[ev - 1] has something to do...
-        */
+//	lwsl_notice("%s: out\n", __func__);
 
 #if defined(LWS_WITH_TLS)
 	if (pt->context->tls_ops &&
@@ -220,110 +164,12 @@
 		pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
 #endif
 
-	/*
-	 * POLLOUT for any fds that can
-	 */
-
 	for (n = 0; n < (int)pt->fds_count; n++)
-		if (pt->fds[n].fd != LWS_SOCK_INVALID &&
-		    pt->fds[n].events & LWS_POLLOUT &&
-		    !pt->fds[n].write_blocked) {
-			struct timeval tv;
-			fd_set se;
-
-			/*
-			 * We have to check if it is blocked...
-			 * if not, do the POLLOUT handling
-			 */
-
-			FD_ZERO(&se);
-			FD_SET(pt->fds[n].fd, &se);
-			tv.tv_sec = tv.tv_usec = 0;
-			if (select(1, NULL, &se, NULL, &tv) != 1)
-				pt->fds[n].write_blocked = 1;
-			else {
-				pt->fds[n].revents |= LWS_POLLOUT;
-				lws_service_fd_tsi(context, &pt->fds[n], tsi);
-			}
+		if (pt->fds[n].fd != LWS_SOCK_INVALID && pt->fds[n].revents) {
+//			lwsl_notice("%s: idx %d, revents 0x%x\n", __func__, n, pt->fds[n].revents);
+			lws_service_fd_tsi(context, &pt->fds[n], tsi);
 		}
 
-       if (ev && ev < WSA_MAXIMUM_WAIT_EVENTS) {
-		unsigned int err;
-
-		/* handle fds[ev - 1] */
-
-               if (WSAEnumNetworkEvents(pt->fds[ev - 1].fd, pt->events[ev],
-				&networkevents) == SOCKET_ERROR) {
-			lwsl_err("WSAEnumNetworkEvents() failed "
-				 "with error %d\n", LWS_ERRNO);
-			return -1;
-		}
-
-		pfd = &pt->fds[ev - 1];
-		pfd->revents = (short)networkevents.lNetworkEvents;
-
-	        if (!pfd->write_blocked && pfd->revents & FD_WRITE)
-	        	 pfd->write_blocked = 0;
-
-		err = networkevents.iErrorCode[FD_CONNECT_BIT];
-		if ((networkevents.lNetworkEvents & FD_CONNECT) &&
-		    wsi_from_fd(context, pfd->fd) &&
-		    !wsi_from_fd(context, pfd->fd)->udp) {
-                       lwsl_debug("%s: FD_CONNECT: %s\n", __func__,
-                		  lws_wsi_tag(wsi_from_fd(context, pfd->fd)));
-			pfd->revents &= ~LWS_POLLOUT;
-			if (err && err != LWS_EALREADY &&
-			    err != LWS_EINPROGRESS &&
-			    err != LWS_EWOULDBLOCK &&
-			    err != WSAEINVAL) {
-				lwsl_debug("Unable to connect errno=%d\n", err);
-
-				/*
-				 * the connection has definitively failed... but
-				 * do we have more DNS entries to try?
-				 */
-				if (wsi_from_fd(context, pfd->fd)->dns_sorted_list.count) {
-					lws_sul_schedule(context, 0,
-						&wsi_from_fd(context, pfd->fd)->
-							sul_connect_timeout,
-						lws_client_conn_wait_timeout, 1);
-                                       return 0;
-                               } else
-					pfd->revents |= LWS_POLLHUP;
-			} else
-                               if (wsi_from_fd(context, pfd->fd)) {
-                                       if (wsi_from_fd(context, pfd->fd)->udp)
-                                               pfd->revents |= LWS_POLLHUP;
-                                       else
-                                               lws_client_connect_3_connect(
-                                        	  wsi_from_fd(context, pfd->fd),
-						  NULL, NULL,
-						  LWS_CONNECT_COMPLETION_GOOD,
-						  NULL);
-                               }
-		}
-
-		if (pfd->revents & LWS_POLLOUT) {
-			wsi = wsi_from_fd(context, pfd->fd);
-			if (wsi)
-				wsi->sock_send_blocking = 0;
-		}
-
-		if (pfd->revents) {
-			/*
-			 * On windows is somehow necessary to "acknowledge" the
-			 * POLLIN event, otherwise we never receive another one
-			 * on the TCP connection.  But it breaks UDP, so only
-			 * do it on non-UDP.
-			 */
-			wsi = wsi_from_fd(context, pfd->fd);
-			if (wsi && !wsi->udp)
-				recv(pfd->fd, NULL, 0, 0);
-
-			lws_service_fd_tsi(context, pfd, tsi);
-		}
-	}
-
 	return 0;
 }
 
diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c
index 00d3658..8196bda 100644
--- a/lib/plat/windows/windows-sockets.c
+++ b/lib/plat/windows/windows-sockets.c
@@ -222,21 +222,7 @@
 lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
 		       struct lws_pollfd *pfd)
 {
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	long e = LWS_POLLHUP | FD_CONNECT | FD_ACCEPT | FD_CLOSE | FD_WRITE;
-
-	/*
-	 * On windows, FD_WRITE is only coming to indicate that we are writable
-	 * again after being choked.  So we must always listen for it.
-	 */
-
-	if (pfd->events & LWS_POLLIN)
-		e |= FD_READ;
-
-	if (WSAEventSelect(wsi->desc.sockfd, pt->events[(pfd - pt->fds) + 1], e)) {
-		lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO);
-		return 1;
-	}
+	//struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 
 	return 0;
 }