From: Bruno Haible Date: Tue, 6 Aug 2024 13:14:25 +0000 (+0200) Subject: windows-mutex, windows-timedmutex: Follow pthread_mutex_trylock spec. X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=d93af6db7c735dabbf4f3c19ba702c8eb2bea1ce;p=gnulib.git windows-mutex, windows-timedmutex: Follow pthread_mutex_trylock spec. * lib/windows-mutex.h (glwthread_mutex_t): Add 'owner' field. * lib/windows-mutex.c: Include . (glwthread_mutex_lock): Set the 'owner' field after entering the critical section. (glwthread_mutex_trylock): Detect whether the lock was previously locked by this thread. Set the 'owner' field after entering the critical section. (glwthread_mutex_unlock): Clear the 'owner' field before leaving the critical section. * lib/windows-timedmutex.h (glwthread_timedmutex_t): Add 'owner' field. * lib/windows-timedmutex.c: (glwthread_timedmutex_lock): Set the 'owner' field after entering the critical section. (glwthread_timedmutex_trylock): Detect whether the lock was previously locked by this thread. Set the 'owner' field after entering the critical section. (glwthread_timedmutex_unlock): Clear the 'owner' field before leaving the critical section. --- diff --git a/ChangeLog b/ChangeLog index ed90d1229d..9d917c6de3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2024-08-06 Bruno Haible + + windows-mutex, windows-timedmutex: Follow pthread_mutex_trylock spec. + * lib/windows-mutex.h (glwthread_mutex_t): Add 'owner' field. + * lib/windows-mutex.c: Include . + (glwthread_mutex_lock): Set the 'owner' field after entering the + critical section. + (glwthread_mutex_trylock): Detect whether the lock was previously locked + by this thread. Set the 'owner' field after entering the critical + section. + (glwthread_mutex_unlock): Clear the 'owner' field before leaving the + critical section. + * lib/windows-timedmutex.h (glwthread_timedmutex_t): Add 'owner' field. + * lib/windows-timedmutex.c: (glwthread_timedmutex_lock): Set the 'owner' + field after entering the critical section. + (glwthread_timedmutex_trylock): Detect whether the lock was previously + locked by this thread. Set the 'owner' field after entering the critical + section. + (glwthread_timedmutex_unlock): Clear the 'owner' field before leaving + the critical section. + 2024-08-05 Bruno Haible gnulib-tool.py: Fix testdirs created with --without-tests. diff --git a/lib/windows-mutex.c b/lib/windows-mutex.c index b112e13b6b..2ac73a0728 100644 --- a/lib/windows-mutex.c +++ b/lib/windows-mutex.c @@ -23,10 +23,12 @@ #include "windows-mutex.h" #include +#include void glwthread_mutex_init (glwthread_mutex_t *mutex) { + mutex->owner = 0; InitializeCriticalSection (&mutex->lock); mutex->guard.done = 1; } @@ -49,7 +51,13 @@ glwthread_mutex_lock (glwthread_mutex_t *mutex) Sleep (0); } } + /* If this thread already owns the mutex, POSIX pthread_mutex_lock() is + required to deadlock here. But let's not do that on purpose. */ EnterCriticalSection (&mutex->lock); + { + DWORD self = GetCurrentThreadId (); + mutex->owner = self; + } return 0; } @@ -72,6 +80,21 @@ glwthread_mutex_trylock (glwthread_mutex_t *mutex) } if (!TryEnterCriticalSection (&mutex->lock)) return EBUSY; + { + DWORD self = GetCurrentThreadId (); + /* TryEnterCriticalSection succeeded. This means that the mutex was either + previously unlocked (and thus mutex->owner == 0) or previously locked by + this thread (and thus mutex->owner == self). Since the mutex is meant to + be plain, we need to fail in the latter case. */ + if (mutex->owner == self) + { + LeaveCriticalSection (&mutex->lock); + return EBUSY; + } + if (mutex->owner != 0) + abort (); + mutex->owner = self; + } return 0; } @@ -80,6 +103,7 @@ glwthread_mutex_unlock (glwthread_mutex_t *mutex) { if (!mutex->guard.done) return EINVAL; + mutex->owner = 0; LeaveCriticalSection (&mutex->lock); return 0; } diff --git a/lib/windows-mutex.h b/lib/windows-mutex.h index 88de4bdcad..cb676c1b90 100644 --- a/lib/windows-mutex.h +++ b/lib/windows-mutex.h @@ -28,6 +28,7 @@ typedef struct { glwthread_initguard_t guard; /* protects the initialization */ + DWORD owner; CRITICAL_SECTION lock; } glwthread_mutex_t; diff --git a/lib/windows-timedmutex.c b/lib/windows-timedmutex.c index 3dfff56dbe..3833cab129 100644 --- a/lib/windows-timedmutex.c +++ b/lib/windows-timedmutex.c @@ -40,6 +40,7 @@ glwthread_timedmutex_init (glwthread_timedmutex_t *mutex) if (event == INVALID_HANDLE_VALUE) return EAGAIN; mutex->event = event; + mutex->owner = 0; InitializeCriticalSection (&mutex->lock); mutex->guard.done = 1; return 0; @@ -72,7 +73,13 @@ glwthread_timedmutex_lock (glwthread_timedmutex_t *mutex) Sleep (0); } } + /* If this thread already owns the mutex, POSIX pthread_mutex_lock() is + required to deadlock here. But let's not do that on purpose. */ EnterCriticalSection (&mutex->lock); + { + DWORD self = GetCurrentThreadId (); + mutex->owner = self; + } return 0; } @@ -104,6 +111,21 @@ glwthread_timedmutex_trylock (glwthread_timedmutex_t *mutex) } if (!TryEnterCriticalSection (&mutex->lock)) return EBUSY; + { + DWORD self = GetCurrentThreadId (); + /* TryEnterCriticalSection succeeded. This means that the mutex was either + previously unlocked (and thus mutex->owner == 0) or previously locked by + this thread (and thus mutex->owner == self). Since the mutex is meant to + be plain, we need to fail in the latter case. */ + if (mutex->owner == self) + { + LeaveCriticalSection (&mutex->lock); + return EBUSY; + } + if (mutex->owner != 0) + abort (); + mutex->owner = self; + } return 0; } @@ -197,6 +219,21 @@ glwthread_timedmutex_timedlock (glwthread_timedmutex_t *mutex, locking it now. */ } } + { + DWORD self = GetCurrentThreadId (); + /* TryEnterCriticalSection succeeded. This means that the mutex was either + previously unlocked (and thus mutex->owner == 0) or previously locked by + this thread (and thus mutex->owner == self). Since the mutex is meant to + be plain, it is useful to fail in the latter case. */ + if (mutex->owner == self) + { + LeaveCriticalSection (&mutex->lock); + return EDEADLK; + } + if (mutex->owner != 0) + abort (); + mutex->owner = self; + } return 0; } @@ -205,6 +242,7 @@ glwthread_timedmutex_unlock (glwthread_timedmutex_t *mutex) { if (!mutex->guard.done) return EINVAL; + mutex->owner = 0; LeaveCriticalSection (&mutex->lock); /* Notify one of the threads that were waiting with a timeout. */ /* SetEvent diff --git a/lib/windows-timedmutex.h b/lib/windows-timedmutex.h index e21879e1a1..6ede4b2408 100644 --- a/lib/windows-timedmutex.h +++ b/lib/windows-timedmutex.h @@ -30,6 +30,7 @@ typedef struct { glwthread_initguard_t guard; /* protects the initialization */ + DWORD owner; HANDLE event; CRITICAL_SECTION lock; }