[v2] eal/windows: Switched to an external pthreads library
diff mbox series

Message ID 20200924114101.174-1-john.alexander@datapath.co.uk
State New
Delegated to: Thomas Monjalon
Headers show
Series
  • [v2] eal/windows: Switched to an external pthreads library
Related show

Checks

Context Check Description
ci/Intel-compilation fail Compilation issues
ci/travis-robot warning Travis build: failed
ci/Performance-Testing fail build patch failure
ci/checkpatch warning coding style issues

Commit Message

John Alexander Sept. 24, 2020, 11:41 a.m. UTC
The Windows EAL build now pulls in the pthreads4w project as an external
subproject.  The pthreads4w subproject does not currently provide a
meson build so the project has been patched with one.

Removed the placeholder librte_eal\windows\include\pthread.h and sched.h
header files as these are superseded by the pthreads4w implementation.

rte_eal_init() in the Windows EAL has been modified to use
pthread_create() and pthread_setaffinity_np() to setup the secondary
lcores.  eal_thread_create() has been removed as it is no longer
required.

rte_eal_init() in the Windows EAL now calls SetPriorityClass() to set
the process class to real-time in a singular location.

eal_thread_loop() in the Windows EAL now calls SetThreadPriority() to
set the thread priority to real-time when the thread commences execution.
This function now uses pthreads functions to test and compare pthread_t
handles.

Added the source file librte_eal\windows\windows_eal_impl.c.  This source
file serves as a location for the implementation of auxiliary
functionality provided by the Windows EAL that would otherwise be provided
by the UNIX runtime.

Added the pthreads4w shared library as a dependency to EAL library builds
for Windows builds only.  The meson install step will install the
pthreads4w.dll that is built by the meson external subproject build step.
The shared library has been selected for both static and shared library
builds to avoid constructior initialisation issues observed when using
pthreads4w and static linkage within the DPDK project.

Added the subprojects folder to the .gitignore file such that authored
content is not ignored and downloaded content is ignored (as that's where
meson stores the subprojects when they are cloned).

Incremented the minimum meson version number to 0.55.0.  This is required
to use the subproject folder patch feature.

Updated MAINTAINERS to reflect new additions.

Signed-off-by: John Alexander <john.alexander@datapath.co.uk>
---
 .gitignore                                    |   6 +
 MAINTAINERS                                   |   3 +
 lib/librte_eal/windows/eal.c                  |  21 ++-
 lib/librte_eal/windows/eal_thread.c           |  27 +---
 lib/librte_eal/windows/eal_windows.h          |  10 --
 lib/librte_eal/windows/include/pthread.h      | 146 ------------------
 lib/librte_eal/windows/include/rte_os.h       |  15 +-
 lib/librte_eal/windows/include/sched.h        |  92 -----------
 lib/librte_eal/windows/meson.build            |   1 +
 lib/librte_eal/windows/windows_eal_impl.c     |  53 +++++++
 lib/meson.build                               |  14 +-
 meson.build                                   |   7 +-
 .../packagefiles/pthreads4w-meson/VERSION     |   1 +
 .../packagefiles/pthreads4w-meson/meson.build |  49 ++++++
 subprojects/pthreads4w-code.wrap              |   8 +
 15 files changed, 178 insertions(+), 275 deletions(-)
 delete mode 100644 lib/librte_eal/windows/include/pthread.h
 delete mode 100644 lib/librte_eal/windows/include/sched.h
 create mode 100644 lib/librte_eal/windows/windows_eal_impl.c
 create mode 100644 subprojects/packagefiles/pthreads4w-meson/VERSION
 create mode 100644 subprojects/packagefiles/pthreads4w-meson/meson.build
 create mode 100644 subprojects/pthreads4w-code.wrap

Patch
diff mbox series

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