diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5299448f3..5db0f0108 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,6 +64,7 @@ option(MUNT "MUNT" ON)
option(VRAMDUMP "Video RAM dumping" OFF)
option(DINPUT "DirectInput" OFF)
option(DISCORD "Discord integration" ON)
+option(CPPTHRAD "C++11 threads" ON)
option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" OFF)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f2d638c2c..142fdfabd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -34,10 +34,14 @@ endif()
# WIN32 marks us as a GUI app on Windows
# MACOSX_BUNDLE prepares a macOS application bundle including with the app icon
-add_executable(86Box WIN32 MACOSX_BUNDLE 86box.c config.c cpp11_thread.cpp log.c random.c timer.c io.c acpi.c apm.c
+add_executable(86Box WIN32 MACOSX_BUNDLE 86box.c config.c log.c random.c timer.c io.c acpi.c apm.c
dma.c ddma.c nmi.c pic.c pit.c port_6x.c port_92.c ppi.c pci.c mca.c usb.c
device.c nvr.c nvr_at.c nvr_ps2.c rtmidi_midi.cpp ${APP_ICON_MACOSX})
+if(CPPTHREADS)
+ target_sources(plat PRIVATE cpp11_thread.cpp)
+endif()
+
if(APPLE)
target_link_libraries(86Box "-framework AppKit")
endif()
diff --git a/src/unix/CMakeLists.txt b/src/unix/CMakeLists.txt
index e21265370..7cd52879b 100644
--- a/src/unix/CMakeLists.txt
+++ b/src/unix/CMakeLists.txt
@@ -8,7 +8,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
else()
set(PLAT_SOURCES unix_midi.c)
endif()
-add_library(plat STATIC ${PLAT_SOURCES} unix_thread.c)
+add_library(plat STATIC ${PLAT_SOURCES})
add_library(ui STATIC unix.c unix_sdl.c unix_cdrom.c)
target_compile_definitions(ui PUBLIC _FILE_OFFSET_BITS=64)
target_link_libraries(ui dl)
@@ -28,6 +28,10 @@ if (ALSA_FOUND)
target_link_libraries(plat ALSA::ALSA)
endif()
-set(THREADS_PREFER_PTHREAD_FLAG TRUE)
-find_package(Threads REQUIRED)
-target_link_libraries(86Box Threads::Threads)
+if (NOT CPPTHREADS)
+ target_sources(plat PRIVATE unix_thread.c)
+
+ set(THREADS_PREFER_PTHREAD_FLAG TRUE)
+ find_package(Threads REQUIRED)
+ target_link_libraries(86Box Threads::Threads)
+endif()
diff --git a/src/win/CMakeLists.txt b/src/win/CMakeLists.txt
index 304ff8d68..9b0016e6f 100644
--- a/src/win/CMakeLists.txt
+++ b/src/win/CMakeLists.txt
@@ -23,6 +23,10 @@ add_library(ui OBJECT win_ui.c win_icon.c win_stbar.c win_sdl.c win_dialog.c win
win_jsconf.c win_media_menu.c win_preferences.c win_discord.c glad.c win_opengl.c
win_opengl_glslp.c 86Box.rc)
+if(NOT CPPTHREADS)
+ target_sources(plat PRIVATE win_thread.c)
+endif()
+
if(MSVC)
# MSVC complains when we include the manifest from 86Box.rc...
# On the bright side, CMake supports passing the manifest as a source
diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw
index 4ef41f2e6..d7bf837da 100644
--- a/src/win/Makefile.mingw
+++ b/src/win/Makefile.mingw
@@ -163,6 +163,9 @@ endif
ifndef DYNAREC
DYNAREC := y
endif
+ifndef CPPTHREADS
+ CPPTHREADS := cpp11
+endif
ifeq ($(DYNAREC), y)
ifeq ($(ARM), y)
ifeq ($(NEW_DYNAREC), n)
@@ -370,6 +373,12 @@ MUNTOBJ := midi_mt32.o \
Synth.o Tables.o TVA.o TVF.o TVP.o sha1.o c_interface.o
endif
+ifeq ($(CPPTHREADS), y)
+THREADOBJ := cpp11_thread.o
+else
+THREADOBJ := win_thread.o
+endif
+
ifeq ($(VNC), y)
OPTS += -DUSE_VNC
RFLAGS += -DUSE_VNC
@@ -464,7 +473,7 @@ CXXFLAGS := $(CFLAGS)
#########################################################################
# Create the (final) list of objects to build. #
#########################################################################
-MAINOBJ := 86box.o config.o cpp11_thread.o log.o random.o timer.o io.o acpi.o apm.o dma.o ddma.o \
+MAINOBJ := 86box.o config.o log.o random.o timer.o io.o acpi.o apm.o dma.o ddma.o \
nmi.o pic.o pit.o port_6x.o port_92.o ppi.o pci.o mca.o \
usb.o device.o nvr.o nvr_at.o nvr_ps2.o rtmidi_midi.o \
$(VNCOBJ)
@@ -693,7 +702,7 @@ endif
OBJ := $(MAINOBJ) $(CPUOBJ) $(CHIPSETOBJ) $(MCHOBJ) $(DEVOBJ) $(MEMOBJ) \
$(FDDOBJ) $(GAMEOBJ) $(CDROMOBJ) $(ZIPOBJ) $(MOOBJ) $(HDDOBJ) $(MINIVHDOBJ) \
$(NETOBJ) $(PRINTOBJ) $(SCSIOBJ) $(SIOOBJ) $(SNDOBJ) $(VIDOBJ) $(VOODOOOBJ) \
- $(PLATOBJ) $(UIOBJ) $(FSYNTHOBJ) $(MUNTOBJ) $(DEVBROBJ) $(MINITRACEOBJ)
+ $(PLATOBJ) $(UIOBJ) $(FSYNTHOBJ) $(MUNTOBJ) $(DEVBROBJ) $(MINITRACEOBJ) $(THREADOBJ)
ifdef EXOBJ
OBJ += $(EXOBJ)
endif
diff --git a/src/win/win_thread.c b/src/win/win_thread.c
new file mode 100644
index 000000000..f8d81fa86
--- /dev/null
+++ b/src/win/win_thread.c
@@ -0,0 +1,183 @@
+/*
+ * 86Box A hypervisor and IBM PC system emulator that specializes in
+ * running old operating systems and software designed for IBM
+ * PC systems and compatibles from 1981 through fairly recent
+ * system designs based on the PCI bus.
+ *
+ * This file is part of the 86Box distribution.
+ *
+ * Implement threads and mutexes for the Win32 platform.
+ *
+ *
+ *
+ * Authors: Sarah Walker,
+ * Fred N. van Kempen,
+ *
+ * Copyright 2008-2018 Sarah Walker.
+ * Copyright 2017,2018 Fred N. van Kempen.
+ */
+#define UNICODE
+#define BITMAP WINDOWS_BITMAP
+#include
+#include
+#include
+#undef BITMAP
+#include
+#include
+#include
+#include
+#include
+#include <86box/86box.h>
+#include <86box/plat.h>
+
+
+typedef struct {
+ HANDLE handle;
+} win_event_t;
+
+
+thread_t *
+thread_create(void (*func)(void *param), void *param)
+{
+ uintptr_t bt = _beginthread(func, 0, param);
+ return((thread_t *)bt);
+}
+
+
+int
+thread_wait(thread_t *arg, int timeout)
+{
+ if (arg == NULL) return(0);
+
+ if (timeout == -1)
+ timeout = INFINITE;
+
+ if (WaitForSingleObject(arg, timeout)) return(1);
+
+ return(0);
+}
+
+
+event_t *
+thread_create_event(void)
+{
+ win_event_t *ev = malloc(sizeof(win_event_t));
+
+ ev->handle = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ return((event_t *)ev);
+}
+
+
+void
+thread_set_event(event_t *arg)
+{
+ win_event_t *ev = (win_event_t *)arg;
+
+ if (arg == NULL) return;
+
+ SetEvent(ev->handle);
+}
+
+
+void
+thread_reset_event(event_t *arg)
+{
+ win_event_t *ev = (win_event_t *)arg;
+
+ if (arg == NULL) return;
+
+ ResetEvent(ev->handle);
+}
+
+
+int
+thread_wait_event(event_t *arg, int timeout)
+{
+ win_event_t *ev = (win_event_t *)arg;
+
+ if (arg == NULL) return(0);
+
+ if (ev->handle == NULL) return(0);
+
+ if (timeout == -1)
+ timeout = INFINITE;
+
+ if (WaitForSingleObject(ev->handle, timeout)) return(1);
+
+ return(0);
+}
+
+
+void
+thread_destroy_event(event_t *arg)
+{
+ win_event_t *ev = (win_event_t *)arg;
+
+ if (arg == NULL) return;
+
+ CloseHandle(ev->handle);
+
+ free(ev);
+}
+
+
+mutex_t *
+thread_create_mutex(void)
+{
+ mutex_t *mutex = malloc(sizeof(CRITICAL_SECTION));
+
+ InitializeCriticalSection(mutex);
+
+ return mutex;
+}
+
+
+mutex_t *
+thread_create_mutex_with_spin_count(unsigned int spin_count)
+{
+ mutex_t *mutex = malloc(sizeof(CRITICAL_SECTION));
+
+ InitializeCriticalSectionAndSpinCount(mutex, spin_count);
+
+ return mutex;
+}
+
+
+int
+thread_wait_mutex(mutex_t *mutex)
+{
+ if (mutex == NULL) return(0);
+
+ LPCRITICAL_SECTION critsec = (LPCRITICAL_SECTION)mutex;
+
+ EnterCriticalSection(critsec);
+
+ return 1;
+}
+
+
+int
+thread_release_mutex(mutex_t *mutex)
+{
+ if (mutex == NULL) return(0);
+
+ LPCRITICAL_SECTION critsec = (LPCRITICAL_SECTION)mutex;
+
+ LeaveCriticalSection(critsec);
+
+ return 1;
+}
+
+
+void
+thread_close_mutex(mutex_t *mutex)
+{
+ if (mutex == NULL) return;
+
+ LPCRITICAL_SECTION critsec = (LPCRITICAL_SECTION)mutex;
+
+ DeleteCriticalSection(critsec);
+
+ free(critsec);
+}