From f708eadcfdcbcea8a83c24ccd069b0955d5710d2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 26 Sep 2017 22:58:59 +0200 Subject: [PATCH] Make the initialization thread-safe Perform the initialization inside a critical section. Unfortunately Windows critical sections, which are the simplest synchronization mechanism available on Windows, do not support static initialization. A call to InitializeCriticalSection is required. Therefore a simple spinlock, with an implementation based on atomic operations, is used as a workaround. --- configure.ac | 1 + src/usbhid.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/configure.ac b/configure.ac index 17bd5f9..d0939b9 100644 --- a/configure.ac +++ b/configure.ac @@ -154,6 +154,7 @@ AC_CHECK_HEADERS([linux/serial.h]) AC_CHECK_HEADERS([IOKit/serial/ioss.h]) AC_CHECK_HEADERS([getopt.h]) AC_CHECK_HEADERS([sys/param.h]) +AC_CHECK_HEADERS([pthread.h]) # Checks for global variable declarations. AC_CHECK_DECLS([optreset]) diff --git a/src/usbhid.c b/src/usbhid.c index 0f7ba7b..f0c10df 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -24,6 +24,13 @@ #endif #include +#ifdef HAVE_PTHREAD_H +#include +#endif +#ifdef _WIN32 +#define NOGDI +#include +#endif #if defined(HAVE_HIDAPI) #define USE_HIDAPI @@ -47,6 +54,14 @@ #include "context-private.h" #include "platform.h" +#ifdef _WIN32 +typedef LONG dc_mutex_t; +#define DC_MUTEX_INIT 0 +#else +typedef pthread_mutex_t dc_mutex_t; +#define DC_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +#endif + struct dc_usbhid_t { /* Library context. */ dc_context_t *context; @@ -87,16 +102,41 @@ syserror(int errcode) #endif #ifdef USBHID +static dc_mutex_t g_usbhid_mutex = DC_MUTEX_INIT; static size_t g_usbhid_refcount = 0; #ifdef USE_LIBUSB static libusb_context *g_usbhid_ctx = NULL; #endif +static void +dc_mutex_lock (dc_mutex_t *mutex) +{ +#ifdef _WIN32 + while (InterlockedCompareExchange (mutex, 1, 0) == 1) { + SleepEx (0, TRUE); + } +#else + pthread_mutex_lock (mutex); +#endif +} + +static void +dc_mutex_unlock (dc_mutex_t *mutex) +{ +#ifdef _WIN32 + InterlockedExchange (mutex, 0); +#else + pthread_mutex_unlock (mutex); +#endif +} + static dc_status_t dc_usbhid_init (dc_context_t *context) { dc_status_t status = DC_STATUS_SUCCESS; + dc_mutex_lock (&g_usbhid_mutex); + if (g_usbhid_refcount == 0) { #if defined(USE_LIBUSB) int rc = libusb_init (&g_usbhid_ctx); @@ -119,12 +159,14 @@ dc_usbhid_init (dc_context_t *context) g_usbhid_refcount++; error: + dc_mutex_unlock (&g_usbhid_mutex); return status; } static dc_status_t dc_usbhid_exit (void) { + dc_mutex_lock (&g_usbhid_mutex); if (--g_usbhid_refcount == 0) { #if defined(USE_LIBUSB) @@ -135,6 +177,8 @@ dc_usbhid_exit (void) #endif } + dc_mutex_unlock (&g_usbhid_mutex); + return DC_STATUS_SUCCESS; } #endif