0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 03:52:22 +02:00

win32: pthread: don't play dirty tricks for mutex init

We used double-checked locking on pthread_mutex_t.requires_init in order
to lazily initialize static mutexes (since CRITICAL_SECTION has no
native way to do this). This was kind of unclean: we relied on MSVC
semantics for volatile (which apparently means all accesses are weakly
atomic), which is not such a good idea since mpv can't even be compiled
with MSVC.

Since it's too much of a pain to get weak atomics, just use INIT_ONCE
for initializing the CRITICAL_SECTION. Microsoft most likely implemented
this in an extremely efficient way. Essentially, it provides a mechanism
for correct double-checked locking without having to deal with the
tricky details. We still use an extra flag to avoid calling it at all
for normal locks.

(To get weak atomics, we could have used stdatomic.h, which modern MinGW
provides just fine. But I don't want this wrapper depend on MinGW
specifics if possible.)
This commit is contained in:
wm4 2015-07-27 22:59:38 +02:00
parent 3452f9aeac
commit b3468d53c7
2 changed files with 10 additions and 28 deletions

View File

@ -25,11 +25,12 @@
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
typedef struct { typedef struct {
volatile LONG requires_init; char static_mutex;
INIT_ONCE static_init;
CRITICAL_SECTION cs; CRITICAL_SECTION cs;
} pthread_mutex_t; } pthread_mutex_t;
#define PTHREAD_MUTEX_INITIALIZER {1} #define PTHREAD_MUTEX_INITIALIZER {1, INIT_ONCE_STATIC_INIT}
#define pthread_mutexattr_t int #define pthread_mutexattr_t int
#define pthread_mutexattr_destroy(attr) (void)0 #define pthread_mutexattr_destroy(attr) (void)0

View File

@ -18,27 +18,6 @@
#include <errno.h> #include <errno.h>
#include <sys/time.h> #include <sys/time.h>
// We keep this around to avoid active waiting while handling static
// initializers.
static pthread_once_t init_cs_once = PTHREAD_ONCE_INIT;
static CRITICAL_SECTION init_cs;
static void init_init_cs(void)
{
InitializeCriticalSection(&init_cs);
}
static void init_lock(void)
{
pthread_once(&init_cs_once, init_init_cs);
EnterCriticalSection(&init_cs);
}
static void init_unlock(void)
{
LeaveCriticalSection(&init_cs);
}
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
{ {
BOOL pending; BOOL pending;
@ -66,12 +45,14 @@ int pthread_mutex_init(pthread_mutex_t *restrict mutex,
int pthread_mutex_lock(pthread_mutex_t *mutex) int pthread_mutex_lock(pthread_mutex_t *mutex)
{ {
if (mutex->requires_init) { if (mutex->static_mutex) {
init_lock(); BOOL pending;
if (mutex->requires_init) if (!InitOnceBeginInitialize(&mutex->static_init, 0, &pending, NULL))
abort();
if (pending) {
InitializeCriticalSection(&mutex->cs); InitializeCriticalSection(&mutex->cs);
_InterlockedAnd(&mutex->requires_init, 0); InitOnceComplete(&mutex->static_init, 0, NULL);
init_unlock(); }
} }
EnterCriticalSection(&mutex->cs); EnterCriticalSection(&mutex->cs);
return 0; return 0;