Merge pull request #469 from compnerd/size

event: create a typedef for the unote ident type
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f530f04..ba0d7af 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -43,7 +43,11 @@
   target_compile_definitions(bsdtests
                              PUBLIC
                                _CRT_NONSTDC_NO_WARNINGS
-                               _CRT_SECURE_NO_WARNINGS)
+                               _CRT_SECURE_NO_WARNINGS
+                               _USE_MATH_DEFINES)
+  target_link_libraries(bsdtests
+                        PUBLIC
+                          bcrypt)
 endif ()
 
 add_executable(bsdtestharness
@@ -165,8 +169,11 @@
        pingpong
        drift
        readsync
-       cascade
-       io)
+       cascade)
+  if(NOT WIN32)
+    # Not ported to Windows yet
+    list(APPEND DISPATCH_C_TESTS io)
+  endif()
   # an oddball; dispatch_priority.c compiled with -DUSE_SET_TARGET_QUEUE=1
   add_unit_test(dispatch_priority2 SOURCES dispatch_priority.c)
   target_compile_options(dispatch_priority2 PRIVATE -DUSE_SET_TARGET_QUEUE=1)
@@ -192,7 +199,9 @@
 add_unit_test(dispatch_plusplus SOURCES dispatch_plusplus.cpp)
 
 # test-specific link options
-if(NOT WIN32)
+if(WIN32)
+  target_link_libraries(dispatch_io_net PRIVATE WS2_32)
+else()
   target_link_libraries(dispatch_group PRIVATE m)
   target_link_libraries(dispatch_timer_short PRIVATE m)
 endif()
diff --git a/tests/dispatch_apply.c b/tests/dispatch_apply.c
index d77d8bb..93c03b9 100644
--- a/tests/dispatch_apply.c
+++ b/tests/dispatch_apply.c
@@ -22,6 +22,11 @@
 #include <stdio.h>
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
 #include <unistd.h>
+#ifdef __ANDROID__
+#include <linux/sysctl.h>
+#else
+#include <sys/sysctl.h>
+#endif /* __ANDROID__ */
 #endif
 #include <stdlib.h>
 #include <assert.h>
@@ -29,11 +34,6 @@
 #include <libkern/OSAtomic.h>
 #endif
 #include <sys/types.h>
-#ifdef __ANDROID__
-#include <linux/sysctl.h>
-#else
-#include <sys/sysctl.h>
-#endif /* __ANDROID__ */
 
 #include <bsdtests.h>
 #include "dispatch_test.h"
@@ -80,6 +80,10 @@
 	uint32_t activecpu;
 #ifdef __linux__
 	activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
+#elif defined(_WIN32)
+	SYSTEM_INFO si;
+	GetSystemInfo(&si);
+	activecpu = si.dwNumberOfProcessors;
 #else
 	size_t s = sizeof(activecpu);
 	sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
diff --git a/tests/dispatch_concur.c b/tests/dispatch_concur.c
index 960f565..74b6c0c 100644
--- a/tests/dispatch_concur.c
+++ b/tests/dispatch_concur.c
@@ -20,17 +20,17 @@
 
 #include <dispatch/dispatch.h>
 #include <dispatch/private.h>
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#include <unistd.h>
-#endif
 #include <stdlib.h>
 #include <stdio.h>
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
 #include <sys/types.h>
+#include <unistd.h>
 #ifdef __ANDROID__
 #include <linux/sysctl.h>
 #else
 #include <sys/sysctl.h>
 #endif /* __ANDROID__ */
+#endif
 
 #include <bsdtests.h>
 #include "dispatch_test.h"
@@ -234,6 +234,10 @@
 
 #ifdef __linux__
 	activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
+#elif defined(_WIN32)
+	SYSTEM_INFO si;
+	GetSystemInfo(&si);
+	activecpu = si.dwNumberOfProcessors;
 #else
 	size_t s = sizeof(activecpu);
 	sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
diff --git a/tests/dispatch_io_net.c b/tests/dispatch_io_net.c
index 83b375e..1e722a8 100644
--- a/tests/dispatch_io_net.c
+++ b/tests/dispatch_io_net.c
@@ -21,17 +21,21 @@
 #include <stdio.h>
 #include <stdlib.h>
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <netdb.h>
+#include <netinet/in.h>
+#include <spawn.h>
+#include <sys/socket.h>
+#include <sys/param.h>
 #include <unistd.h>
+#elif defined(_WIN32)
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+#include <Windows.h>
 #endif
 #include <errno.h>
-#include <netdb.h>
 #include <sys/types.h>
-#include <sys/param.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <spawn.h>
 #ifdef __APPLE__
 #include <crt_externs.h>
 #include <mach-o/dyld.h>
@@ -41,7 +45,9 @@
 #include "dispatch_test.h"
 #include <dispatch/dispatch.h>
 
+#if !defined(_WIN32)
 extern char **environ;
+#endif
 
 #ifndef DISPATCHTEST_IO
 #if DISPATCH_API_VERSION >= 20100226 && DISPATCH_API_VERSION != 20101110
@@ -49,22 +55,39 @@
 #endif
 #endif
 
-#if defined(__linux__) || defined(__FreeBSD__)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32)
 #define _NSGetExecutablePath(ef,bs) (*(bs)=(size_t)snprintf(ef,*(bs),"%s",argv[0]),0)
 #endif
 
+#if defined(_WIN32)
+typedef USHORT in_port_t;
+#endif
+
+#if !defined(_WIN32)
+#define closesocket(x) close(x)
+#endif
+
 #if DISPATCHTEST_IO
 int
 main(int argc, char** argv)
 {
 	struct hostent *he;
 	int sockfd = -1, clientfd = -1;
-	int read_fd = -1, fd = -1;
+	dispatch_fd_t read_fd = -1, fd = -1;
 	struct sockaddr_in addr1, addr2, server;
 	socklen_t addr2len;
 	socklen_t addr1len;
 	pid_t clientid;
 
+#if defined(_WIN32)
+	WSADATA wsa;
+	int err = WSAStartup(MAKEWORD(2, 2), &wsa);
+	if (err != 0) {
+		fprintf(stderr, "WSAStartup failed with %d\n", err);
+		test_stop();
+	}
+#endif
+
 	if (argc == 3) {
 		// Client
 		dispatch_test_start(NULL);
@@ -93,7 +116,7 @@
 		// Read from the socket and compare the contents are what we expect
 
 		const char *path = argv[2];
-		fd = open(path, O_RDONLY);
+		fd = dispatch_test_fd_open(path, O_RDONLY);
 		if (fd == -1) {
 			test_errno("client-open", errno, 0);
 			test_stop();
@@ -114,12 +137,8 @@
 		// investigate what the impact of lack of file cache disabling has 
 		// for this test
 #endif
-		struct stat sb;
-		if (fstat(fd, &sb)) {
-			test_errno("client-fstat", errno, 0);
-			test_stop();
-		}
-		size_t size = (size_t)sb.st_size;
+		size_t size = (size_t)dispatch_test_fd_lseek(fd, 0, SEEK_END);
+		dispatch_test_fd_lseek(fd, 0, SEEK_SET);
 
 		__block dispatch_data_t g_d1 = dispatch_data_empty;
 		__block dispatch_data_t g_d2 = dispatch_data_empty;
@@ -167,8 +186,8 @@
 				 memcmp(dict_contig_buf, socket_contig_buf,
 				 MIN(dict_contig_size, socket_contig_size)), 0);
 
-		close(fd);
-		close(sockfd);
+		dispatch_test_fd_close(fd);
+		closesocket(sockfd);
 		dispatch_release(g_d1);
 		dispatch_release(g_d2);
 		dispatch_release(dict_data);
@@ -223,7 +242,7 @@
 		// unlink the file as soon as it can, so the server must open it before
 		// starting the client process.
 		char *path = dispatch_test_get_large_file();
-		read_fd = open(path, O_RDONLY);
+		read_fd = dispatch_test_fd_open(path, O_RDONLY);
 		if (read_fd == -1) {
 			test_errno("open", errno, 0);
 			goto stop_test;
@@ -242,6 +261,23 @@
 			test_errno("Server-posix_spawnp()", error, 0);
 			goto stop_test;
 		}
+#elif defined(_WIN32)
+		WCHAR *cmdline = argv_to_command_line(arguments);
+		if (!cmdline) {
+			fprintf(stderr, "argv_to_command_line() failed\n");
+			test_stop();
+		}
+		STARTUPINFOW si = {.cb = sizeof(si)};
+		PROCESS_INFORMATION pi;
+		BOOL created = CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL,
+				NULL, &si, &pi);
+		DWORD error = GetLastError();
+		free(cmdline);
+		if (!created) {
+			print_winapi_error("CreateProcessW", error);
+			test_stop();
+		}
+		clientid = (pid_t)pi.dwProcessId;
 #elif defined(__unix__)
 		clientid = fork();
 		if (clientid == -1) {
@@ -275,12 +311,8 @@
 		// investigate what the impact of lack of file cache disabling has 
 		// for this test
 #endif
-		struct stat sb;
-		if (fstat(read_fd, &sb)) {
-			test_errno("fstat", errno, 0);
-			goto stop_test;
-		}
-		size_t size = (size_t)sb.st_size;
+		size_t size = (size_t)dispatch_test_fd_lseek(read_fd, 0, SEEK_END);
+		dispatch_test_fd_lseek(read_fd, 0, SEEK_SET);
 
 		dispatch_group_t g = dispatch_group_create();
 		dispatch_group_enter(g);
@@ -294,9 +326,9 @@
 			// convenience method handlers should only be called once
 			if (dispatch_data_get_size(d)!= size) {
 				fprintf(stderr, "Reading of data didn't complete\n");
-				close(read_fd);
-				close(clientfd);
-				close(sockfd);
+				dispatch_test_fd_close(read_fd);
+				closesocket(clientfd);
+				closesocket(sockfd);
 				test_stop();
 			}
 			dispatch_group_enter(g);
@@ -308,21 +340,21 @@
 				if (remaining) {
 					fprintf(stderr, "Server-dispatch_write() incomplete .. "
 							"%zu bytes\n", dispatch_data_get_size(remaining));
-					close(read_fd);
-					close(clientfd);
-					close(sockfd);
+					dispatch_test_fd_close(read_fd);
+					closesocket(clientfd);
+					closesocket(sockfd);
 					test_stop();
 				}
-				close(clientfd); // Sending the client EOF
+				closesocket(clientfd); // Sending the client EOF
 				dispatch_group_leave(g);
 			});
-			close(read_fd);
+			dispatch_test_fd_close(read_fd);
 			dispatch_group_leave(g);
 		});
 		test_group_wait(g);
 		dispatch_release(g);
 		fprintf(stderr, "Shutting down server\n");
-		close(sockfd);
+		closesocket(sockfd);
 		free(path);
 		test_stop();
 
@@ -331,9 +363,9 @@
 			dispatch_test_release_large_file(path);
 			free(path);
 		}
-		close(read_fd);
-		close(clientfd);
-		close(sockfd);
+		dispatch_test_fd_close(read_fd);
+		closesocket(clientfd);
+		closesocket(sockfd);
 		test_stop();
 	}
 }
diff --git a/tests/dispatch_priority.c b/tests/dispatch_priority.c
index c500e87..d1a9e18 100644
--- a/tests/dispatch_priority.c
+++ b/tests/dispatch_priority.c
@@ -23,6 +23,11 @@
 #include <dispatch/private.h>
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
 #include <unistd.h>
+#ifdef __ANDROID__
+#include <linux/sysctl.h>
+#else
+#include <sys/sysctl.h>
+#endif /* __ANDROID__ */
 #endif
 #include <stdlib.h>
 #include <assert.h>
@@ -30,11 +35,6 @@
 #include <TargetConditionals.h>
 #endif
 #include <sys/types.h>
-#ifdef __ANDROID__
-#include <linux/sysctl.h>
-#else
-#include <sys/sysctl.h>
-#endif /* __ANDROID__ */
 
 #include <bsdtests.h>
 #include "dispatch_test.h"
@@ -85,6 +85,10 @@
 	dispatch_once(&pred, ^{
 #ifdef __linux__
 		n = (int)sysconf(_SC_NPROCESSORS_CONF);
+#elif defined(_WIN32)
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+		n = (int)si.dwNumberOfProcessors;
 #else
 		size_t l = sizeof(n);
 		int rc = sysctlbyname("hw.ncpu", &n, &l, NULL, 0);
diff --git a/tests/dispatch_read.c b/tests/dispatch_read.c
index b891d2e..23545b5 100644
--- a/tests/dispatch_read.c
+++ b/tests/dispatch_read.c
@@ -18,7 +18,6 @@
  * @APPLE_APACHE_LICENSE_HEADER_END@
  */
 
-#include <sys/stat.h>
 #include <sys/types.h>
 #include <assert.h>
 #include <fcntl.h>
@@ -48,27 +47,26 @@
 main(void)
 {
 	char *path = dispatch_test_get_large_file();
-	struct stat sb;
 
 	dispatch_test_start("Dispatch Source Read");
 
-	int infd = open(path, O_RDONLY);
+	dispatch_fd_t infd = dispatch_test_fd_open(path, O_RDONLY);
 	if (infd == -1) {
 		perror(path);
 		exit(EXIT_FAILURE);
 	}
 	dispatch_test_release_large_file(path);
 	free(path);
-	if (fstat(infd, &sb) == -1) {
-		perror(path);
-		exit(EXIT_FAILURE);
-	}
-	bytes_total = (size_t)sb.st_size;
 
+	bytes_total = (size_t)dispatch_test_fd_lseek(infd, 0, SEEK_END);
+	dispatch_test_fd_lseek(infd, 0, SEEK_SET);
+
+#if !defined(_WIN32)
 	if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) {
 		perror(path);
 		exit(EXIT_FAILURE);
 	}
+#endif
 
 	if (!dispatch_test_check_evfilt_read_for_fd(infd)) {
 		test_skip("EVFILT_READ kevent not firing for test file");
@@ -88,11 +86,11 @@
 		test_double_less_than_or_equal("estimated", estimated, bytes_total - bytes_read);
 		const ssize_t bufsiz = 1024*500; // 500 KB buffer
 		static char buffer[1024*500];	// 500 KB buffer
-		ssize_t actual = read(infd, buffer, sizeof(buffer));
+		ssize_t actual = dispatch_test_fd_read(infd, buffer, sizeof(buffer));
 		bytes_read += (size_t)actual;
 		printf("bytes read: %zd\n", actual);
 		if (actual < bufsiz) {
-			actual = read(infd, buffer, sizeof(buffer));
+			actual = dispatch_test_fd_read(infd, buffer, sizeof(buffer));
 			bytes_read += (size_t)actual;
 			// confirm EOF condition
 			test_long("EOF", actual, 0);
@@ -102,7 +100,7 @@
 
 	dispatch_source_set_cancel_handler(reader, ^{
 		test_sizet("Bytes read", bytes_read, bytes_total);
-		int res = close(infd);
+		int res = dispatch_test_fd_close(infd);
 		test_errno("close", res == -1 ? errno : 0, 0);
 		dispatch_release(reader);
 	});
diff --git a/tests/dispatch_read2.c b/tests/dispatch_read2.c
index d20c758..0e45359 100644
--- a/tests/dispatch_read2.c
+++ b/tests/dispatch_read2.c
@@ -20,16 +20,16 @@
 
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/param.h>
 #include <assert.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <fts.h>
+#include <sys/param.h>
 #include <unistd.h>
 #endif
 #include <errno.h>
-#include <fts.h>
 #ifdef __APPLE__
 #include <mach/mach.h>
 #include <mach/mach_time.h>
@@ -68,10 +68,12 @@
 			  dispatch_queue_t queue,
 			  void (^handler)(dispatch_data_t d, int error))
 {
+#if !defined(_WIN32)
 	if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
 		test_errno("fcntl O_NONBLOCK", errno, 0);
 		test_stop();
 	}
+#endif
 	dispatch_source_t reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
 			(uintptr_t)fd, 0, queue);
 	test_ptr_notnull("reader", reader);
@@ -82,16 +84,28 @@
 	dispatch_source_set_event_handler(reader, ^{
 		const ssize_t bufsiz = 1024*512; // 512KB buffer
 		char *buffer = NULL;
+#if defined(_WIN32)
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+		size_t pagesize = (size_t)si.dwPageSize;
+		buffer = _aligned_malloc(bufsiz, pagesize);
+#else
 		size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
 		posix_memalign((void **)&buffer, pagesize, bufsiz);
-		ssize_t actual = read(fd, buffer, bufsiz);
+#endif
+		ssize_t actual = dispatch_test_fd_read(fd, buffer, bufsiz);
 		if (actual == -1) {
 			err = errno;
 		}
 		if (actual > 0) {
 			bytes_read += (size_t)actual;
+#if defined(_WIN32)
+			dispatch_data_t tmp_data = dispatch_data_create(buffer, (size_t)actual,
+					NULL, ^{ _aligned_free(buffer); });
+#else
 			dispatch_data_t tmp_data = dispatch_data_create(buffer, (size_t)actual,
 					NULL, DISPATCH_DATA_DESTRUCTOR_FREE);
+#endif
 			dispatch_data_t concat = dispatch_data_create_concat(data,tmp_data);
 			dispatch_release(tmp_data);
 			dispatch_release(data);
@@ -100,7 +114,7 @@
 		// If we reached EOF or we read as much we were asked to.
 		if (actual < bufsiz || bytes_read >= length) {
 			char foo[2];
-			actual = read(fd, foo, 2);
+			actual = dispatch_test_fd_read(fd, foo, 2);
 			bytes_read += (size_t)actual;
 			// confirm EOF condition
 			test_long("EOF", actual, 0);
@@ -123,7 +137,7 @@
 test_read(void)
 {
 	char *path = dispatch_test_get_large_file();
-	int fd = open(path, O_RDONLY);
+	dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
 	if (fd == -1) {
 		test_errno("open", errno, 0);
 		test_stop();
@@ -139,12 +153,8 @@
 	// investigate what the impact of lack of file cache disabling has 
 	// for this test
 #endif
-	struct stat sb;
-	if (fstat(fd, &sb)) {
-		test_errno("fstat", errno, 0);
-		test_stop();
-	}
-	size_t size = (size_t)sb.st_size;
+	size_t size = (size_t)dispatch_test_fd_lseek(fd, 0, SEEK_END);
+	dispatch_test_fd_lseek(fd, 0, SEEK_SET);
 	dispatch_group_t g = dispatch_group_create();
 	void (^b)(dispatch_data_t, int) = ^(dispatch_data_t d, int error) {
 		test_errno("read error", error, 0);
@@ -158,7 +168,7 @@
 			if (contig_size) {
 				// Validate the copied buffer is similar to what we expect
 				char *buf = (char*)malloc(size);
-				pread(fd, buf, size, 0);
+				dispatch_test_fd_pread(fd, buf, size, 0);
 				test_long("dispatch data contents", memcmp(buf, contig_buf,
 						size), 0);
 				free(buf);
@@ -170,7 +180,7 @@
 	dispatch_group_enter(g);
 	dispatch_read(fd, SIZE_MAX, dispatch_get_global_queue(0, 0), b); // rdar://problem/7795794
 	test_group_wait(g);
-	lseek(fd, 0, SEEK_SET);
+	dispatch_test_fd_lseek(fd, 0, SEEK_SET);
 	if (dispatch_test_check_evfilt_read_for_fd(fd)) {
 		dispatch_group_enter(g);
 		dispatch_read2(fd, size, dispatch_get_global_queue(0,0), b);
@@ -179,22 +189,39 @@
 		test_skip("EVFILT_READ kevent not firing for test file");
 	}
 	dispatch_release(g);
-	close(fd);
+	dispatch_test_fd_close(fd);
 }
 
 static void
 test_read_write(void)
 {
-	const char *path_in = "/dev/urandom";
-	char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
-	const size_t siz_in = 10240;
+#if defined(_WIN32)
+	char *path_in = dispatch_test_get_large_file();
+	char path_out[] = "dispatchtest_io.XXXXXX";
 
-	int in = open(path_in, O_RDONLY);
+	dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
 	if (in == -1) {
 		test_errno("open", errno, 0);
 		test_stop();
 	}
-	int out = mkstemp(path_out);
+	dispatch_test_release_large_file(path_in);
+	free(path_in);
+
+	size_t siz_in = (size_t)dispatch_test_fd_lseek(in, 0, SEEK_END);
+	dispatch_test_fd_lseek(in, 0, SEEK_SET);
+#else
+	const char *path_in = "/dev/urandom";
+	char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
+	const size_t siz_in = 10240;
+
+	dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
+	if (in == -1) {
+		test_errno("open", errno, 0);
+		test_stop();
+	}
+#endif
+
+	dispatch_fd_t out = mkstemp(path_out);
 	if (out == -1) {
 		test_errno("mkstemp", errno, 0);
 		test_stop();
@@ -203,6 +230,7 @@
 		test_errno("unlink", errno, 0);
 		test_stop();
 	}
+
 	dispatch_queue_t q = dispatch_get_global_queue(0,0);
 	dispatch_group_t g = dispatch_group_create();
 	dispatch_group_enter(g);
@@ -212,7 +240,7 @@
 			test_errno("dispatch_read", err_in, 0);
 			test_stop();
 		}
-		close(in);
+		dispatch_test_fd_close(in);
 		size_t siz_out = dispatch_data_get_size(data_in);
 		test_sizet("read size", siz_out, siz_in);
 		dispatch_retain(data_in);
@@ -222,14 +250,14 @@
 				test_errno("dispatch_write", err_out, 0);
 				test_stop();
 			}
-			lseek(out, 0, SEEK_SET);
+			dispatch_test_fd_lseek(out, 0, SEEK_SET);
 			dispatch_read(out, siz_out, q,
 					^(dispatch_data_t cmp, int err_cmp) {
 				if (err_cmp) {
 					test_errno("dispatch_read", err_cmp, 0);
 					test_stop();
 				}
-				close(out);
+				dispatch_test_fd_close(out);
 				size_t siz_cmp = dispatch_data_get_size(cmp);
 				test_sizet("readback size", siz_cmp, siz_out);
 				const void *data_buf, *cmp_buf;
@@ -252,17 +280,32 @@
 static void
 test_read_writes(void) // <rdar://problem/7785143>
 {
-	const char *path_in = "/dev/urandom";
-	char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
 	const size_t chunks_out = 320;
 	const size_t siz_chunk = 32, siz_in = siz_chunk * chunks_out;
 
-	int in = open(path_in, O_RDONLY);
+#if defined(_WIN32)
+	char *path_in = dispatch_test_get_large_file();
+	char path_out[] = "dispatchtest_io.XXXXXX";
+
+	dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
 	if (in == -1) {
 		test_errno("open", errno, 0);
 		test_stop();
 	}
-	int out = mkstemp(path_out);
+	dispatch_test_release_large_file(path_in);
+	free(path_in);
+#else
+	const char *path_in = "/dev/urandom";
+	char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
+
+	dispatch_fd_t in = dispatch_test_fd_open(path_in, O_RDONLY);
+	if (in == -1) {
+		test_errno("open", errno, 0);
+		test_stop();
+	}
+#endif
+
+	dispatch_fd_t out = mkstemp(path_out);
 	if (out == -1) {
 		test_errno("mkstemp", errno, 0);
 		test_stop();
@@ -271,6 +314,7 @@
 		test_errno("unlink", errno, 0);
 		test_stop();
 	}
+
 	dispatch_queue_t q = dispatch_get_global_queue(0,0);
 	dispatch_group_t g = dispatch_group_create();
 	dispatch_group_enter(g);
@@ -281,7 +325,7 @@
 			test_errno("dispatch_read", err_in, 0);
 			test_stop();
 		}
-		close(in);
+		dispatch_test_fd_close(in);
 		siz_out = dispatch_data_get_size(data_in);
 		test_sizet("read size", siz_out, siz_in);
 		dispatch_retain(data_in);
@@ -311,14 +355,14 @@
 	});
 	test_group_wait(g);
 	dispatch_group_enter(g);
-	lseek(out, 0, SEEK_SET);
+	dispatch_test_fd_lseek(out, 0, SEEK_SET);
 	dispatch_read(out, siz_in, q,
 			^(dispatch_data_t cmp, int err_cmp) {
 		if (err_cmp) {
 			test_errno("dispatch_read", err_cmp, 0);
 			test_stop();
 		}
-		close(out);
+		dispatch_test_fd_close(out);
 		size_t siz_cmp = dispatch_data_get_size(cmp);
 		test_sizet("readback size", siz_cmp, siz_out);
 		const void *data_buf, *cmp_buf;
@@ -336,6 +380,7 @@
 	dispatch_release(g);
 }
 
+#if !defined(_WIN32)
 static void
 test_writes_reads_eagain(void) // rdar://problem/8333366
 {
@@ -395,6 +440,7 @@
 	Block_release(b);
 	dispatch_release(g);
 }
+#endif
 
 #endif // DISPATCHTEST_IO
 
@@ -408,8 +454,10 @@
 		test_read();
 		test_read_write();
 		test_read_writes();
+#if !defined(_WIN32)
 		test_writes_reads_eagain();
 #endif
+#endif
 		test_fin(NULL);
 	});
 	dispatch_main();
diff --git a/tests/dispatch_readsync.c b/tests/dispatch_readsync.c
index 293387a..c543875 100644
--- a/tests/dispatch_readsync.c
+++ b/tests/dispatch_readsync.c
@@ -23,12 +23,12 @@
 #include <stdlib.h>
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
 #include <unistd.h>
-#endif
 #ifdef __ANDROID__
 #include <linux/sysctl.h>
 #else
 #include <sys/sysctl.h>
 #endif /* __ANDROID__ */
+#endif
 #include <assert.h>
 
 #include <bsdtests.h>
@@ -143,6 +143,11 @@
 	activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
 	// don't want to parse /proc/sys/kernel/threads-max
 	wq_max_threads = activecpu * NTHREADS + 2;
+#elif defined(_WIN32)
+	SYSTEM_INFO si;
+	GetSystemInfo(&si);
+	activecpu = si.dwNumberOfProcessors;
+	wq_max_threads = activecpu * NTHREADS + 2;
 #else
 	size_t s = sizeof(uint32_t);
 	sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
diff --git a/tests/dispatch_select.c b/tests/dispatch_select.c
index f7d4f55..66601ae 100644
--- a/tests/dispatch_select.c
+++ b/tests/dispatch_select.c
@@ -39,14 +39,25 @@
 void
 stage1(int stage)
 {
+#if defined(_WIN32)
+	char *path = dispatch_test_get_large_file();
+	dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
+	if (fd == -1)
+	{
+		perror(path);
+		exit(EXIT_FAILURE);
+	}
+	dispatch_test_release_large_file(path);
+	free(path);
+#else
 	const char *path = "/dev/random";
-
 	int fd = open(path, O_RDONLY);
 	if (fd == -1)
 	{
 		perror(path);
 		exit(EXIT_FAILURE);
 	}
+#endif
 
 	dispatch_queue_t main_q = dispatch_get_main_queue();
 	test_ptr_notnull("main_q", main_q);
@@ -57,13 +68,13 @@
 	dispatch_source_set_event_handler(source, ^{
 		size_t buffer_size = 500*1024;
 		char buffer[500*1024];
-		ssize_t sz = read(fd, buffer, buffer_size);
+		ssize_t sz = dispatch_test_fd_read(fd, buffer, buffer_size);
 		test_double_less_than_or_equal("kevent read 1", sz, buffer_size+1);
 		dispatch_source_cancel(source);
 	});
 
 	dispatch_source_set_cancel_handler(source, ^{
-		int res = close(fd);
+		int res = dispatch_test_fd_close(fd);
 		test_errno("close", res ==  -1 ? errno : 0, 0);
 		dispatch_release(source);
 		if (stage == 1)
@@ -87,9 +98,7 @@
 stage2(void)
 {
 	char *path = dispatch_test_get_large_file();
-	struct stat sb;
-
-	int fd = open(path, O_RDONLY);
+	dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY);
 	if (fd == -1)
 	{
 		perror(path);
@@ -100,20 +109,15 @@
 
 	if (!dispatch_test_check_evfilt_read_for_fd(fd)) {
 		test_skip("EVFILT_READ kevent not firing for test file");
-		close(fd);
+		dispatch_test_fd_close(fd);
 		dispatch_async(dispatch_get_main_queue(), ^{
 			stage1(3);
 		});
 		return;
 	}
 
-	if (fstat(fd, &sb) == -1)
-	{
-		perror(path);
-		exit(EXIT_FAILURE);
-	}
-
-	ssize_t expected = sb.st_size;
+	ssize_t expected = dispatch_test_fd_lseek(fd, 0, SEEK_END);
+	dispatch_test_fd_lseek(fd, 0, SEEK_SET);
 	actual = 0;
 
 	dispatch_queue_t main_q = dispatch_get_main_queue();
@@ -126,11 +130,11 @@
 		size_t est = dispatch_source_get_data(source);
 		test_double_less_than_or_equal("estimated", est, expected - actual);
 		char buffer[500*1024];
-		ssize_t sz = read(fd, buffer, sizeof(buffer));
+		ssize_t sz = dispatch_test_fd_read(fd, buffer, sizeof(buffer));
 		actual += sz;
 		if (sz < (ssize_t)sizeof(buffer))
 		{
-			sz = read(fd, buffer, sizeof(buffer));
+			sz = dispatch_test_fd_read(fd, buffer, sizeof(buffer));
 			actual += sz;
 			test_long("EOF", sz, 0);
 			dispatch_source_cancel(source);
@@ -139,7 +143,7 @@
 
 	dispatch_source_set_cancel_handler(source, ^{
 		test_long("bytes read", actual, expected);
-		int res = close(fd);
+		int res = dispatch_test_fd_close(fd);
 		test_errno("close", res ==  -1 ? errno : 0, 0);
 		dispatch_release(source);
 		dispatch_async(dispatch_get_main_queue(), ^{
diff --git a/tests/dispatch_test.c b/tests/dispatch_test.c
index 9809543..b0d028d 100644
--- a/tests/dispatch_test.c
+++ b/tests/dispatch_test.c
@@ -25,6 +25,7 @@
 #include <objc/objc-auto.h>
 #endif
 
+#include <fcntl.h>
 #include <stdlib.h>
 #include <stdio.h>
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
@@ -36,8 +37,8 @@
 #include <sys/poll.h>
 #endif
 #elif defined(_WIN32)
-#include <io.h>
 #include <Windows.h>
+#include <bcrypt.h>
 #endif
 #include <assert.h>
 
@@ -55,7 +56,7 @@
 }
 
 bool
-dispatch_test_check_evfilt_read_for_fd(int fd)
+dispatch_test_check_evfilt_read_for_fd(dispatch_fd_t fd)
 {
 #if HAS_SYS_EVENT_H
 	int kq = kqueue();
@@ -72,7 +73,7 @@
 	close(kq);
 	return r > 0;
 #elif defined(_WIN32)
-	HANDLE handle = (HANDLE)_get_osfhandle(fd);
+	HANDLE handle = (HANDLE)fd;
 	// A zero-distance move retrieves the file pointer
 	LARGE_INTEGER currentPosition;
 	LARGE_INTEGER distance = {.QuadPart = 0};
@@ -104,14 +105,28 @@
 {
 #if defined(__APPLE__)
 	return strdup("/usr/bin/vi");
-#elif defined(__unix__)
+#elif defined(__unix__) || defined(_WIN32)
 	// Depending on /usr/bin/vi being present is unreliable (especially on
 	// Android), so fill up a large-enough temp file with random bytes
 
+#if defined(_WIN32)
+	char temp_dir_buf[MAX_PATH];
+	const char *temp_dir = getenv("TEMP") ?: getenv("TMP");
+	if (!temp_dir) {
+		DWORD len = GetTempPathA(sizeof(temp_dir_buf), temp_dir_buf);
+		if (len > 0 && len < sizeof(temp_dir_buf)) {
+			temp_dir = temp_dir_buf;
+		} else {
+			temp_dir = ".";
+		}
+	}
+#else
 	const char *temp_dir = getenv("TMPDIR");
 	if (temp_dir == NULL || temp_dir[0] == '\0') {
 		temp_dir = "/tmp";
 	}
+#endif
+
 	const char *const suffix = "/dispatch_test.XXXXXX";
 	size_t temp_dir_len = strlen(temp_dir);
 	size_t suffix_len = strlen(suffix);
@@ -119,7 +134,7 @@
 	assert(path != NULL);
 	memcpy(path, temp_dir, temp_dir_len);
 	memcpy(&path[temp_dir_len], suffix, suffix_len + 1);
-	int temp_fd = mkstemp(path);
+	dispatch_fd_t temp_fd = mkstemp(path);
 	if (temp_fd == -1) {
 		perror("mkstemp");
 		exit(EXIT_FAILURE);
@@ -129,12 +144,22 @@
 	char *file_buf = malloc(file_size);
 	assert(file_buf != NULL);
 
+	ssize_t num;
+#if defined(_WIN32)
+	NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)file_buf, file_size,
+			BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+	if (status < 0) {
+		fprintf(stderr, "BCryptGenRandom failed with %ld\n", status);
+		dispatch_test_release_large_file(path);
+		exit(EXIT_FAILURE);
+	}
+#else
 	int urandom_fd = open("/dev/urandom", O_RDONLY);
 	if (urandom_fd == -1) {
 		perror("/dev/urandom");
+		dispatch_test_release_large_file(path);
 		exit(EXIT_FAILURE);
 	}
-	ssize_t num;
 	size_t pos = 0;
 	while (pos < file_size) {
 		num = read(urandom_fd, &file_buf[pos], file_size - pos);
@@ -142,26 +167,25 @@
 			pos += (size_t)num;
 		} else if (num == -1 && errno != EINTR) {
 			perror("read");
+			dispatch_test_release_large_file(path);
 			exit(EXIT_FAILURE);
 		}
 	}
 	close(urandom_fd);
+#endif
 
 	do {
-		num = write(temp_fd, file_buf, file_size);
+		num = dispatch_test_fd_write(temp_fd, file_buf, file_size);
 	} while (num == -1 && errno == EINTR);
 	if (num == -1) {
 		perror("write");
+		dispatch_test_release_large_file(path);
 		exit(EXIT_FAILURE);
 	}
 	assert(num == file_size);
-	close(temp_fd);
+	dispatch_test_fd_close(temp_fd);
 	free(file_buf);
 	return path;
-#elif defined(_WIN32)
-	// TODO
-	fprintf(stderr, "dispatch_test_get_large_file() not implemented on Windows\n");
-	abort();
 #else
 #error "dispatch_test_get_large_file not implemented on this platform"
 #endif
@@ -173,10 +197,10 @@
 #if defined(__APPLE__)
 	// The path is fixed to a system file - do nothing
 	(void)path;
-#elif defined(__unix__)
-	unlink(path);
-#elif defined(_WIN32)
-	// TODO
+#elif defined(__unix__) || defined(_WIN32)
+	if (unlink(path) < 0) {
+		perror("unlink");
+	}
 #else
 #error "dispatch_test_release_large_file not implemented on this platform"
 #endif
@@ -188,3 +212,155 @@
 	dispatch_queue_t actual = dispatch_get_current_queue();
 	_test_ptr(file, line, desc, actual, expected);
 }
+
+dispatch_fd_t
+dispatch_test_fd_open(const char *path, int flags)
+{
+#if defined(_WIN32)
+	DWORD desired_access = 0;
+	DWORD creation_disposition = OPEN_EXISTING;
+	switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
+		case O_RDONLY:
+			desired_access = GENERIC_READ;
+			break;
+		case O_WRONLY:
+			desired_access = GENERIC_WRITE;
+			break;
+		case O_RDWR:
+			desired_access = GENERIC_READ | GENERIC_WRITE;
+			break;
+	}
+	if (flags & O_CREAT) {
+		creation_disposition = OPEN_ALWAYS;
+		if (flags & O_EXCL) {
+			creation_disposition = CREATE_NEW;
+		}
+	}
+	// FILE_SHARE_DELETE is important here because tests must be able to delete
+	// temporary files after opening them
+	HANDLE handle = CreateFileA(path, desired_access,
+			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			/* lpSecurityAttributes */ NULL, creation_disposition,
+			/* dwFlagsAndAttributes */ 0, /* hTemplateFile */ NULL);
+	if (handle == INVALID_HANDLE_VALUE) {
+		DWORD error = GetLastError();
+		switch (error) {
+			case ERROR_ACCESS_DENIED:
+				errno = EACCES;
+				break;
+			case ERROR_FILE_EXISTS:
+				errno = EEXIST;
+				break;
+			case ERROR_FILE_NOT_FOUND:
+			case ERROR_PATH_NOT_FOUND:
+				errno = ENOENT;
+				break;
+			default:
+				print_winapi_error("CreateFileA", GetLastError());
+				errno = EIO;
+				break;
+		}
+		return -1;
+	}
+	return (dispatch_fd_t)handle;
+#else
+	return open(path, flags);
+#endif
+}
+
+int
+dispatch_test_fd_close(dispatch_fd_t fd)
+{
+#if defined(_WIN32)
+	if (!CloseHandle((HANDLE)fd)) {
+		errno = EBADF;
+		return -1;
+	}
+	return 0;
+#else
+	return close(fd);
+#endif
+}
+
+off_t
+dispatch_test_fd_lseek(dispatch_fd_t fd, off_t offset, int whence)
+{
+#if defined(_WIN32)
+	DWORD method;
+	switch (whence) {
+		case SEEK_CUR:
+			method = FILE_CURRENT;
+			break;
+		case SEEK_END:
+			method = FILE_END;
+			break;
+        case SEEK_SET:
+        default:
+            method = FILE_BEGIN;
+            break;
+	}
+	LARGE_INTEGER distance = {.QuadPart = offset};
+	LARGE_INTEGER new_pos;
+	if (!SetFilePointerEx((HANDLE)fd, distance, &new_pos, method)) {
+		print_winapi_error("SetFilePointerEx", GetLastError());
+		errno = EINVAL;
+		return -1;
+	}
+	return (off_t)new_pos.QuadPart;
+#else
+	return lseek(fd, offset, whence);
+#endif
+}
+
+ssize_t
+dispatch_test_fd_pread(dispatch_fd_t fd, void *buf, size_t count, off_t offset)
+{
+#if defined(_WIN32)
+	OVERLAPPED overlapped;
+	memset(&overlapped, 0, sizeof(overlapped));
+	LARGE_INTEGER lioffset = {.QuadPart = offset};
+	overlapped.Offset = lioffset.LowPart;
+	overlapped.OffsetHigh = lioffset.HighPart;
+	DWORD num_read;
+	if (!ReadFile((HANDLE)fd, buf, count, &num_read, &overlapped)) {
+		print_winapi_error("ReadFile", GetLastError());
+		errno = EIO;
+		return -1;
+	}
+	return (ssize_t)num_read;
+#else
+	return pread(fd, buf, count, offset);
+#endif
+}
+
+ssize_t
+dispatch_test_fd_read(dispatch_fd_t fd, void *buf, size_t count)
+{
+#if defined(_WIN32)
+	DWORD num_read;
+	if (!ReadFile((HANDLE)fd, buf, count, &num_read, NULL)) {
+		print_winapi_error("ReadFile", GetLastError());
+		errno = EIO;
+		return -1;
+	}
+	return (ssize_t)num_read;
+#else
+	return read(fd, buf, count);
+#endif
+}
+
+ssize_t
+dispatch_test_fd_write(dispatch_fd_t fd, const void *buf, size_t count)
+{
+#if defined(_WIN32)
+	DWORD num_written;
+	if (!WriteFile((HANDLE)fd, buf, count, &num_written, NULL)) {
+		print_winapi_error("WriteFile", GetLastError());
+		errno = EIO;
+		return -1;
+	}
+	return (ssize_t)num_written;
+#else
+	return write(fd, buf, count);
+#endif
+}
diff --git a/tests/dispatch_test.h b/tests/dispatch_test.h
index 415e419..9de95b3 100644
--- a/tests/dispatch_test.h
+++ b/tests/dispatch_test.h
@@ -40,7 +40,7 @@
 
 void dispatch_test_start(const char* desc);
 
-bool dispatch_test_check_evfilt_read_for_fd(int fd);
+bool dispatch_test_check_evfilt_read_for_fd(dispatch_fd_t fd);
 
 char *dispatch_test_get_large_file(void);
 void dispatch_test_release_large_file(const char *path);
@@ -53,6 +53,13 @@
 		size_t *newpl);
 #endif
 
+dispatch_fd_t dispatch_test_fd_open(const char *path, int flags);
+int dispatch_test_fd_close(dispatch_fd_t fd);
+off_t dispatch_test_fd_lseek(dispatch_fd_t fd, off_t offset, int whence);
+ssize_t dispatch_test_fd_pread(dispatch_fd_t fd, void *buf, size_t count, off_t offset);
+ssize_t dispatch_test_fd_read(dispatch_fd_t fd, void *buf, size_t count);
+ssize_t dispatch_test_fd_write(dispatch_fd_t fd, const void *buf, size_t count);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif
diff --git a/tests/generic_win_port.c b/tests/generic_win_port.c
index f84f9f9..ba685a1 100644
--- a/tests/generic_win_port.c
+++ b/tests/generic_win_port.c
@@ -1,4 +1,6 @@
+#define _CRT_RAND_S
 #include <generic_win_port.h>
+#include <dispatch_test.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -220,6 +222,42 @@
 	return result * 100;  // Convert from 100ns units
 }
 
+static void
+randomize_name(char *out)
+{
+	static const char chars[] =
+			"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-";
+	const size_t num_chars = sizeof(chars) - 1;
+	unsigned int lo, hi;
+	rand_s(&lo);
+	rand_s(&hi);
+	uint64_t val = ((uint64_t)hi << 32) | lo;
+	for (int j = 0; j < 6; j++) {
+		out[j] = chars[val % num_chars];
+		val /= num_chars;
+	}
+}
+
+dispatch_fd_t
+mkstemp(char *tmpl)
+{
+	size_t len = strlen(tmpl);
+	if (len < 6) {
+		errno = EINVAL;
+		return -1;
+	}
+	char *replace = &tmpl[len - 6];
+	for (int i = 0; i < 100; i++) {
+		randomize_name(replace);
+		dispatch_fd_t fd = dispatch_test_fd_open(tmpl, O_RDWR | O_CREAT | O_EXCL);
+		if (fd != -1) {
+			return fd;
+		}
+	}
+	errno = EEXIST;
+	return -1;
+}
+
 void
 print_winapi_error(const char *function_name, DWORD error)
 {
@@ -236,6 +274,14 @@
 	}
 }
 
+intptr_t
+random(void)
+{
+	unsigned int x;
+	rand_s(&x);
+	return x & INT_MAX;
+}
+
 unsigned int
 sleep(unsigned int seconds)
 {
diff --git a/tests/generic_win_port.h b/tests/generic_win_port.h
index 41c076c..d693c74 100644
--- a/tests/generic_win_port.h
+++ b/tests/generic_win_port.h
@@ -1,6 +1,9 @@
 #pragma once
 
+#include <dispatch/dispatch.h>
+#include <fcntl.h>
 #include <stdint.h>
+#include <sys/types.h>
 #include <Windows.h>
 
 typedef int kern_return_t;
@@ -65,9 +68,15 @@
 	return 0;
 }
 
+dispatch_fd_t
+mkstemp(char *tmpl);
+
 void
 print_winapi_error(const char *function_name, DWORD error);
 
+intptr_t
+random(void);
+
 unsigned int
 sleep(unsigned int seconds);