diff --git a/.gitignore b/.gitignore
index f73d93ca5..3792a843b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,9 @@ build-*
 # ignore other build directory patterns
 *-gcc*
 *-clang*
+
+# ignore sub-directories of the subprojects directory excluding wrap files and the packagefiles directory
+subprojects/**/*
+!subprojects/*.wrap
+!subprojects/packagefiles/
+!subprojects/packagefiles/**
diff --git a/MAINTAINERS b/MAINTAINERS
index c0abbe0fc..c33c1bc72 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -318,10 +318,13 @@ M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
 M: Narcisa Ana Maria Vasile <navasile@linux.microsoft.com>
 M: Dmitry Malloy <dmitrym@microsoft.com>
 M: Pallavi Kadam <pallavi.kadam@intel.com>
+M: John Alexander <john.alexander@datapath.co.uk>
 F: lib/librte_eal/windows/
+F: lib/librte_eal/windows/windows_eal_impl.c
 F: lib/librte_eal/rte_eal_exports.def
 F: buildtools/map_to_win.py
 F: doc/guides/windows_gsg/
+F: subprojects/
 
 Windows memory allocation
 M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
diff --git a/lib/librte_eal/windows/eal.c b/lib/librte_eal/windows/eal.c
index bc48f27ab..28320a769 100644
--- a/lib/librte_eal/windows/eal.c
+++ b/lib/librte_eal/windows/eal.c
@@ -260,7 +260,7 @@ rte_eal_cleanup(void)
 int
 rte_eal_init(int argc, char **argv)
 {
-	int i, fctret, bscan;
+	int i, fctret, bscan, ret;
 	const struct rte_config *config = rte_eal_get_configuration();
 	struct internal_config *internal_conf =
 		eal_get_internal_configuration();
@@ -360,6 +360,15 @@ rte_eal_init(int argc, char **argv)
 		return -1;
 	}
 
+	/*
+	 * We require real-time priority threads.  To achieve this on Windows we must
+	 * set the current process class to real-time.  Setting the process class to
+	 * real-time requires elevated privileges (admin user) otherwise the maximum
+	 * achievable thread priority will still only be 15 (out of 31) even with
+	 * real-time thread priority and real-time process class set.
+	 */
+	SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
+
 	RTE_LCORE_FOREACH_SLAVE(i) {
 
 		/*
@@ -376,8 +385,16 @@ rte_eal_init(int argc, char **argv)
 		lcore_config[i].state = WAIT;
 
 		/* create a thread for each lcore */
-		if (eal_thread_create(&lcore_config[i].thread_id) != 0)
+		/* create a thread for each lcore */
+		ret = pthread_create(&lcore_config[i].thread_id, NULL,
+					eal_thread_loop, NULL);
+		if (ret != 0)
 			rte_panic("Cannot create thread\n");
+
+		ret = pthread_setaffinity_np(lcore_config[i].thread_id,
+		sizeof(rte_cpuset_t), &lcore_config[i].cpuset);
+		if (ret != 0)
+			rte_panic("Cannot set affinity\n");
 	}
 
 	/* Initialize services so drivers can register services during probe. */
diff --git a/lib/librte_eal/windows/eal_thread.c b/lib/librte_eal/windows/eal_thread.c
index 20889b619..24b68d184 100644
--- a/lib/librte_eal/windows/eal_thread.c
+++ b/lib/librte_eal/windows/eal_thread.c
@@ -3,6 +3,7 @@
  */
 
 #include <io.h>
+#include <pthread.h>
 
 #include <rte_atomic.h>
 #include <rte_debug.h>
@@ -66,9 +67,11 @@ eal_thread_loop(void *arg __rte_unused)
 
 	thread_id = pthread_self();
 
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+
 	/* retrieve our lcore_id from the configuration structure */
 	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
-		if (thread_id == lcore_config[lcore_id].thread_id)
+		if (pthread_equal(thread_id, lcore_config[lcore_id].thread_id))
 			break;
 	}
 	if (lcore_id == RTE_MAX_LCORE)
@@ -79,8 +82,8 @@ eal_thread_loop(void *arg __rte_unused)
 
 	__rte_thread_init(lcore_id, &lcore_config[lcore_id].cpuset);
 
-	RTE_LOG(DEBUG, EAL, "lcore %u is ready (tid=%zx;cpuset=[%s])\n",
-		lcore_id, (uintptr_t)thread_id, cpuset);
+	RTE_LOG(DEBUG, EAL, "lcore %u is ready (tid=%p;cpuset=[%s])\n",
+		lcore_id, thread_id, cpuset);
 
 	/* read on our pipe to get commands */
 	while (1) {
@@ -122,24 +125,6 @@ eal_thread_loop(void *arg __rte_unused)
 	}
 }
 
-/* function to create threads */
-int
-eal_thread_create(pthread_t *thread)
-{
-	HANDLE th;
-
-	th = CreateThread(NULL, 0,
-		(LPTHREAD_START_ROUTINE)(ULONG_PTR)eal_thread_loop,
-						NULL, 0, (LPDWORD)thread);
-	if (!th)
-		return -1;
-
-	SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
-	SetThreadPriority(th, THREAD_PRIORITY_TIME_CRITICAL);
-
-	return 0;
-}
-
 /* get current thread ID */
 int
 rte_sys_gettid(void)
diff --git a/lib/librte_eal/windows/eal_windows.h b/lib/librte_eal/windows/eal_windows.h
index d48ee0a12..dfd3801ff 100644
--- a/lib/librte_eal/windows/eal_windows.h
+++ b/lib/librte_eal/windows/eal_windows.h
@@ -35,16 +35,6 @@
  */
 int eal_create_cpu_map(void);
 
-/**
- * Create a thread.
- *
- * @param thread
- *   The location to store the thread id if successful.
- * @return
- *   0 for success, -1 if the thread is not created.
- */
-int eal_thread_create(pthread_t *thread);
-
 /**
  * Get system NUMA node number for a socket ID.
  *
diff --git a/lib/librte_eal/windows/include/pthread.h b/lib/librte_eal/windows/include/pthread.h
deleted file mode 100644
index 99013dc94..000000000
--- a/lib/librte_eal/windows/include/pthread.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2019 Intel Corporation
- */
-
-#ifndef _PTHREAD_H_
-#define _PTHREAD_H_
-
-#include <stdint.h>
-#include <sched.h>
-
-/**
- * This file is required to support the common code in eal_common_proc.c,
- * eal_common_thread.c and common\include\rte_per_lcore.h as Microsoft libc
- * does not contain pthread.h. This may be removed in future releases.
- */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <rte_common.h>
-#include <rte_windows.h>
-
-#define PTHREAD_BARRIER_SERIAL_THREAD TRUE
-
-/* defining pthread_t type on Windows since there is no in Microsoft libc*/
-typedef uintptr_t pthread_t;
-
-/* defining pthread_attr_t type on Windows since there is no in Microsoft libc*/
-typedef void *pthread_attr_t;
-
-typedef SYNCHRONIZATION_BARRIER pthread_barrier_t;
-
-#define pthread_barrier_init(barrier, attr, count) \
-	InitializeSynchronizationBarrier(barrier, count, -1)
-#define pthread_barrier_wait(barrier) EnterSynchronizationBarrier(barrier, \
-	SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY)
-#define pthread_barrier_destroy(barrier) \
-	DeleteSynchronizationBarrier(barrier)
-#define pthread_cancel(thread) TerminateThread((HANDLE) thread, 0)
-
-/* pthread function overrides */
-#define pthread_self() \
-	((pthread_t)GetCurrentThreadId())
-
-static inline int
-pthread_setaffinity_np(pthread_t threadid, size_t cpuset_size,
-			rte_cpuset_t *cpuset)
-{
-	DWORD_PTR ret = 0;
-	HANDLE thread_handle;
-
-	if (cpuset == NULL || cpuset_size == 0)
-		return -1;
-
-	thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadid);
-	if (thread_handle == NULL) {
-		RTE_LOG_WIN32_ERR("OpenThread()");
-		return -1;
-	}
-
-	ret = SetThreadAffinityMask(thread_handle, *cpuset->_bits);
-	if (ret == 0) {
-		RTE_LOG_WIN32_ERR("SetThreadAffinityMask()");
-		goto close_handle;
-	}
-
-close_handle:
-	if (CloseHandle(thread_handle) == 0) {
-		RTE_LOG_WIN32_ERR("CloseHandle()");
-		return -1;
-	}
-	return (ret == 0) ? -1 : 0;
-}
-
-static inline int
-pthread_getaffinity_np(pthread_t threadid, size_t cpuset_size,
-			rte_cpuset_t *cpuset)
-{
-	/* Workaround for the lack of a GetThreadAffinityMask()
-	 *API in Windows
-	 */
-	DWORD_PTR prev_affinity_mask;
-	HANDLE thread_handle;
-	DWORD_PTR ret = 0;
-
-	if (cpuset == NULL || cpuset_size == 0)
-		return -1;
-
-	thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadid);
-	if (thread_handle == NULL) {
-		RTE_LOG_WIN32_ERR("OpenThread()");
-		return -1;
-	}
-
-	/* obtain previous mask by setting dummy mask */
-	prev_affinity_mask = SetThreadAffinityMask(thread_handle, 0x1);
-	if (prev_affinity_mask == 0) {
-		RTE_LOG_WIN32_ERR("SetThreadAffinityMask()");
-		goto close_handle;
-	}
-
-	/* set it back! */
-	ret = SetThreadAffinityMask(thread_handle, prev_affinity_mask);
-	if (ret == 0) {
-		RTE_LOG_WIN32_ERR("SetThreadAffinityMask()");
-		goto close_handle;
-	}
-
-	memset(cpuset, 0, cpuset_size);
-	*cpuset->_bits = prev_affinity_mask;
-
-close_handle:
-	if (CloseHandle(thread_handle) == 0) {
-		RTE_LOG_WIN32_ERR("SetThreadAffinityMask()");
-		return -1;
-	}
-	return (ret == 0) ? -1 : 0;
-}
-
-static inline int
-pthread_create(void *threadid, const void *threadattr, void *threadfunc,
-		void *args)
-{
-	RTE_SET_USED(threadattr);
-	HANDLE hThread;
-	hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadfunc,
-		args, 0, (LPDWORD)threadid);
-	if (hThread) {
-		SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
-		SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
-	}
-	return ((hThread != NULL) ? 0 : E_FAIL);
-}
-
-static inline int
-pthread_join(__rte_unused pthread_t thread,
-	__rte_unused void **value_ptr)
-{
-	return 0;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PTHREAD_H_ */
diff --git a/lib/librte_eal/windows/include/rte_os.h b/lib/librte_eal/windows/include/rte_os.h
index 569ed92d5..09d2aa17e 100644
--- a/lib/librte_eal/windows/include/rte_os.h
+++ b/lib/librte_eal/windows/include/rte_os.h
@@ -15,6 +15,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sched.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -43,10 +44,22 @@ extern "C" {
 #define unlink _unlink
 
 /* cpu_set macros implementation */
+typedef cpu_set_t rte_cpuset_t;
 #define RTE_CPU_AND(dst, src1, src2) CPU_AND(dst, src1, src2)
 #define RTE_CPU_OR(dst, src1, src2) CPU_OR(dst, src1, src2)
 #define RTE_CPU_FILL(set) CPU_FILL(set)
-#define RTE_CPU_NOT(dst, src) CPU_NOT(dst, src)
+void RTE_CPU_ANDNOT(cpu_set_t *dst, cpu_set_t *src);
+void RTE_CPU_COPY(cpu_set_t *from, cpu_set_t *to);
+void RTE_CPU_FILL(cpu_set_t *pdestset);
+#define RTE_CPU_NOT(dst, src) do \
+{ \
+	cpu_set_t tmp; \
+	RTE_CPU_FILL(&tmp); \
+	RTE_CPU_ANDNOT(&tmp, src); \
+	RTE_CPU_COPY(&tmp, dst); \
+} while (0)
+
+
 
 /* as in <windows.h> */
 typedef long long ssize_t;
diff --git a/lib/librte_eal/windows/include/sched.h b/lib/librte_eal/windows/include/sched.h
deleted file mode 100644
index fbe07f742..000000000
--- a/lib/librte_eal/windows/include/sched.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2019 Intel Corporation
- */
-
-#ifndef _SCHED_H_
-#define _SCHED_H_
-
-/**
- * This file is added to support the common code in eal_common_thread.c
- * as Microsoft libc does not contain sched.h. This may be removed
- * in future releases.
- */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef CPU_SETSIZE
-#define CPU_SETSIZE RTE_MAX_LCORE
-#endif
-
-#define _BITS_PER_SET (sizeof(long long) * 8)
-#define _BIT_SET_MASK (_BITS_PER_SET - 1)
-
-#define _NUM_SETS(b) (((b) + _BIT_SET_MASK) / _BITS_PER_SET)
-#define _WHICH_SET(b) ((b) / _BITS_PER_SET)
-#define _WHICH_BIT(b) ((b) & (_BITS_PER_SET - 1))
-
-typedef struct _rte_cpuset_s {
-	long long _bits[_NUM_SETS(CPU_SETSIZE)];
-} rte_cpuset_t;
-
-#define CPU_SET(b, s) ((s)->_bits[_WHICH_SET(b)] |= (1LL << _WHICH_BIT(b)))
-
-#define CPU_ZERO(s)							\
-	do {								\
-		unsigned int _i;					\
-									\
-		for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++)		\
-			(s)->_bits[_i] = 0LL;				\
-	} while (0)
-
-#define CPU_ISSET(b, s) (((s)->_bits[_WHICH_SET(b)] & \
-	(1LL << _WHICH_BIT(b))) != 0LL)
-
-static inline int
-count_cpu(rte_cpuset_t *s)
-{
-	unsigned int _i;
-	int count = 0;
-
-	for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++)
-		if (CPU_ISSET(_i, s) != 0LL)
-			count++;
-	return count;
-}
-#define CPU_COUNT(s) count_cpu(s)
-
-#define CPU_AND(dst, src1, src2) \
-do { \
-	unsigned int _i; \
-	\
-	for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++) \
-		(dst)->_bits[_i] = (src1)->_bits[_i] & (src2)->_bits[_i]; \
-} while (0)
-
-#define CPU_OR(dst, src1, src2) \
-do { \
-	unsigned int _i; \
-	\
-	for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++) \
-		(dst)->_bits[_i] = (src1)->_bits[_i] | (src2)->_bits[_i]; \
-} while (0)
-
-#define CPU_FILL(s) \
-do { \
-	unsigned int _i; \
-	for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++) \
-		(s)->_bits[_i] = -1LL; \
-} while (0)
-
-#define CPU_NOT(dst, src) \
-do { \
-	unsigned int _i; \
-	for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++) \
-		(dst)->_bits[_i] = (src)->_bits[_i] ^ -1LL; \
-} while (0)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SCHED_H_ */
diff --git a/lib/librte_eal/windows/meson.build b/lib/librte_eal/windows/meson.build
index b690bc6b0..9f5b7839a 100644
--- a/lib/librte_eal/windows/meson.build
+++ b/lib/librte_eal/windows/meson.build
@@ -18,6 +18,7 @@ sources += files(
 	'eal_timer.c',
 	'fnmatch.c',
 	'getopt.c',
+	'windows_eal_impl.c'
 )
 
 dpdk_conf.set10('RTE_EAL_NUMA_AWARE_HUGEPAGES', true)
diff --git a/lib/librte_eal/windows/windows_eal_impl.c b/lib/librte_eal/windows/windows_eal_impl.c
new file mode 100644
index 000000000..bb7414532
--- /dev/null
+++ b/lib/librte_eal/windows/windows_eal_impl.c
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2020 Datapath Limited
+ */
+
+#include "eal_windows.h"
+
+pid_t
+getpid()
+{
+	return GetCurrentProcessId();
+}
+
+void
+RTE_CPU_FILL(cpu_set_t *pdestset)
+{
+	SYSTEM_INFO system_info;
+	memset(&system_info, 0, sizeof(system_info));
+
+	GetSystemInfo(&system_info);
+
+	int masked_so_far = 0;
+	int index = 0;
+	do{
+		unsigned char mask = 0xFF;
+
+		if ((masked_so_far + 8) > system_info.dwNumberOfProcessors){
+			int mask_shift = (masked_so_far + 8) - system_info.dwNumberOfProcessors;
+			mask = mask >> mask_shift;
+		}
+
+		pdestset->cpuset[index] = mask;
+
+		masked_so_far += 8;
+		index++;
+	} while (masked_so_far < system_info.dwNumberOfProcessors);
+}
+
+void
+RTE_CPU_ANDNOT(cpu_set_t *dst, cpu_set_t *src)
+{
+	int set_index;
+	for (set_index = 0;
+	     set_index < _countof(dst->cpuset);
+	     set_index++){
+		dst->cpuset[set_index] = ~(dst->cpuset[set_index] & src->cpuset[set_index]);
+	}
+}
+
+void
+RTE_CPU_COPY(cpu_set_t *from, cpu_set_t *to)
+{
+	memcpy(to, from, sizeof(cpu_set_t));
+}
diff --git a/lib/meson.build b/lib/meson.build
index d8b358e5f..fd154ec5d 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -43,6 +43,11 @@ if is_windows
 		'ring',
 		'mempool', 'mbuf', 'net', 'meter', 'ethdev', 'pci',
 	] # only supported libraries for windows
+
+	# Windows doesn't natively support pthreads so we pull down an external project
+	#   using the meson subproject feature.
+	pthreads4w_proj = subproject('pthreads4w-code')
+	pthreads4w_shared_dep = pthreads4w_proj.get_variable('pthreads4w_shared_dep')
 endif
 
 default_cflags = machine_args
@@ -82,6 +87,12 @@ foreach l:libraries
 	if build
 		shared_deps = ext_deps
 		static_deps = ext_deps
+		if is_windows
+			# Initialisation issues with the external pthreads library when linked with
+			#  DPDK require us to always use the shared build of the pthreads4w library.
+			shared_deps += pthreads4w_shared_dep
+			static_deps += pthreads4w_shared_dep
+		endif
 		foreach d:deps
 			if not is_variable('shared_rte_' + d)
 				error('Missing internal dependency "@0@" for @1@ [@2@]'
@@ -153,8 +164,7 @@ foreach l:libraries
 				output: '@0@_mingw.map'.format(libname))
 
 			if is_ms_linker
-				lk_args = ['-Wl,/def:' + def_file.full_path(),
-					'-Wl,/implib:lib\\' + implib]
+				lk_args = ['-Wl,/def:' + def_file.full_path()]
 			else
 				if is_windows
 					lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
diff --git a/meson.build b/meson.build
index 61d9a4f5f..9e061c73b 100644
--- a/meson.build
+++ b/meson.build
@@ -8,7 +8,7 @@ project('DPDK', 'C',
 		files('VERSION')).stdout().strip(),
 	license: 'BSD',
 	default_options: ['buildtype=release', 'default_library=static'],
-	meson_version: '>= 0.47.1'
+	meson_version: '>= 0.55.0'
 )
 
 # set up some global vars for compiler, platform, configuration, etc.
@@ -44,6 +44,11 @@ global_inc = include_directories('.', 'config',
 subdir('buildtools')
 subdir('config')
 
+if is_windows
+	# Windows builds do not define this but it is required so we define it globally here.
+	add_global_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c')
+endif
+
 # build libs and drivers
 subdir('buildtools/pmdinfogen')
 subdir('lib')
diff --git a/subprojects/packagefiles/pthreads4w-meson/VERSION b/subprojects/packagefiles/pthreads4w-meson/VERSION
new file mode 100644
index 000000000..4a36342fc
--- /dev/null
+++ b/subprojects/packagefiles/pthreads4w-meson/VERSION
@@ -0,0 +1 @@
+3.0.0
diff --git a/subprojects/packagefiles/pthreads4w-meson/meson.build b/subprojects/packagefiles/pthreads4w-meson/meson.build
new file mode 100644
index 000000000..738a51133
--- /dev/null
+++ b/subprojects/packagefiles/pthreads4w-meson/meson.build
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2020 Datapath Limited
+
+project('pthreads4w', 'C',
+	# Fallback to "more" for Windows compatibility.
+	version: run_command(find_program('cat', 'more'),
+		files('VERSION')).stdout().strip(),
+	license: 'APACHE2.0',
+	default_options: ['buildtype=release', 'default_library=static'],
+	meson_version: '>= 0.55.0'
+)
+
+sources = files(
+	'pthread.c'
+)
+
+objs = []
+cflags = ['-DHAVE_CONFIG_H']
+
+# The static library is built differently to ensure the process attach/detach and thread attach/detach operate correctly.
+cflags_static = cflags
+cflags_static += '-D__PTW32_STATIC_LIB'
+pthreads4w_static_lib = static_library('pthreads4w',
+      sources,
+      objects: objs,
+      c_args: cflags_static,
+      install: true)
+
+pthreads4w_shared_lib = shared_library('pthreads4w',
+	sources,
+	objects: objs,
+	c_args: cflags,
+	implib: true,
+	install: true)
+
+pthreads4w_static_dep = declare_dependency(
+	include_directories: '',
+	link_with : pthreads4w_static_lib)
+
+pthreads4w_shared_dep = declare_dependency(
+	include_directories: '',
+	link_with : pthreads4w_shared_lib)
+
+install_headers(
+	 'pthread.h',
+	 '_ptw32.h',
+	 'sched.h',
+	 'semaphore.h')
+
diff --git a/subprojects/pthreads4w-code.wrap b/subprojects/pthreads4w-code.wrap
new file mode 100644
index 000000000..d2215e5ce
--- /dev/null
+++ b/subprojects/pthreads4w-code.wrap
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2020 Datapath Limited
+
+[wrap-git]
+directory=pthreads4w
+url=git://git.code.sf.net/p/pthreads4w/code
+revision=Version-3-0-0-release
+patch_directory=pthreads4w-meson
