From: Bruno Haible <bruno@clisp.org>
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=0cd5825421831dc132e335a5b5cd7648bb2e334e;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 <stdlib.h>.
(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 dc27670bda..d7e5c44ddc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2024-08-06  Bruno Haible  <bruno@clisp.org>
+
+	windows-mutex, windows-timedmutex: Follow pthread_mutex_trylock spec.
+	* lib/windows-mutex.h (glwthread_mutex_t): Add 'owner' field.
+	* lib/windows-mutex.c: Include <stdlib.h>.
+	(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  <bruno@clisp.org>
 
 	Improve a comment.
diff --git a/lib/windows-mutex.c b/lib/windows-mutex.c
index b112e13b6b..d258dbc1a9 100644
--- a/lib/windows-mutex.c
+++ b/lib/windows-mutex.c
@@ -23,6 +23,7 @@
 #include "windows-mutex.h"
 
 #include <errno.h>
+#include <stdlib.h>
 
 void
 glwthread_mutex_init (glwthread_mutex_t *mutex)
@@ -49,7 +50,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 +79,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 +102,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..1beb56df62 100644
--- a/lib/windows-timedmutex.c
+++ b/lib/windows-timedmutex.c
@@ -72,7 +72,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 +110,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 +218,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 +241,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;
         }