]> Savannah Git Hosting - gnulib.git/commitdiff
pthread-rwlock: New module.
authorBruno Haible <bruno@clisp.org>
Mon, 15 Jul 2019 00:37:47 +0000 (02:37 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 15 Jul 2019 00:38:34 +0000 (02:38 +0200)
* lib/pthread-rwlock.c: New file, based on lib/glthread/lock.c.
* m4/pthread-rwlock.m4: New file.
* modules/pthread-rwlock: New file.
* doc/posix-functions/pthread_rwlock_init.texi: Mention the new module
and the Android problem.
* doc/posix-functions/pthread_rwlockattr_init.texi: Likewise.
* doc/posix-functions/pthread_rwlockattr_destroy.texi: Likewise.
* doc/posix-functions/pthread_rwlock_rdlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_wrlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_tryrdlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_trywrlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_timedrdlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_timedwrlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_unlock.texi: Likewise.
* doc/posix-functions/pthread_rwlock_destroy.texi: Likewise.

16 files changed:
ChangeLog
doc/posix-functions/pthread_mutex_init.texi
doc/posix-functions/pthread_rwlock_destroy.texi
doc/posix-functions/pthread_rwlock_init.texi
doc/posix-functions/pthread_rwlock_rdlock.texi
doc/posix-functions/pthread_rwlock_timedrdlock.texi
doc/posix-functions/pthread_rwlock_timedwrlock.texi
doc/posix-functions/pthread_rwlock_tryrdlock.texi
doc/posix-functions/pthread_rwlock_trywrlock.texi
doc/posix-functions/pthread_rwlock_unlock.texi
doc/posix-functions/pthread_rwlock_wrlock.texi
doc/posix-functions/pthread_rwlockattr_destroy.texi
doc/posix-functions/pthread_rwlockattr_init.texi
lib/pthread-rwlock.c [new file with mode: 0644]
m4/pthread-rwlock.m4 [new file with mode: 0644]
modules/pthread-rwlock [new file with mode: 0644]

index 7f778a9f474885f761f86d6a1531351e8ce2828d..1b0786db3c02202f154fe6d1155b3a0eace3831d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2019-07-14  Bruno Haible  <bruno@clisp.org>
+
+       pthread-rwlock: New module.
+       * lib/pthread-rwlock.c: New file, based on lib/glthread/lock.c.
+       * m4/pthread-rwlock.m4: New file.
+       * modules/pthread-rwlock: New file.
+       * doc/posix-functions/pthread_rwlock_init.texi: Mention the new module
+       and the Android problem.
+       * doc/posix-functions/pthread_rwlockattr_init.texi: Likewise.
+       * doc/posix-functions/pthread_rwlockattr_destroy.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_rdlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_wrlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_tryrdlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_trywrlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_timedrdlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_timedwrlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_unlock.texi: Likewise.
+       * doc/posix-functions/pthread_rwlock_destroy.texi: Likewise.
+
 2019-07-14  Bruno Haible  <bruno@clisp.org>
 
        pthread-mutex: New module.
index fb86ed2f0291366e521bc6232836feb8d10492ba..4c4dc1fc06dc3edaea65760cfadfd0fcea2cbfe2 100644 (file)
@@ -10,7 +10,7 @@ Portability problems fixed by Gnulib:
 @itemize
 @item
 This function is missing on some platforms:
-Minix 3.1.8, mingw, MSVC 14.
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
 But the provided replacement is just a dummy on some of these platforms:
 Minix 3.1.8.
 @end itemize
index 5293bcbaee36c84df523a17fa6ab52d14d2d053e..92a928d8c2712cca1217ffd0a35b530e75fd98c1 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_destroy.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
 @end itemize
index 10f5edac5ec5f689d35ce6985c06ca9d06dd4b26..15270656e1ed37d9d5296ee84509144f028af05e 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_init.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, mingw, MSVC 14.
 @end itemize
index 5c451938f9e2e84e9b7e53cb5c8384479f1c22d3..ccd22b6d9228d60057145a42b8e8b08449500159 100644 (file)
@@ -4,18 +4,20 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
 @item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
-@item
 This function prefers readers to writers (meaning, when this function is
 called on an rwlock that is already taken by one or more readers, and
 another writer is already waiting to take it, this function may return
index a12024b5979d2bb58f4f817ecbfb8b15862783af..f1039cddc1b80e9acb192d8583d1a925f4b6981b 100644 (file)
@@ -4,18 +4,20 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_timedrdlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Mac OS X 10.5, FreeBSD 5.2.1, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 9, Cygwin, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
 @item
-This function is missing on some platforms:
-Mac OS X 10.5, FreeBSD 5.2.1, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 9, Cygwin, mingw, MSVC 14.
-@item
 This function prefers readers to writers (meaning, when this function is
 called on an rwlock that is already taken by one or more readers, and
 another writer is already waiting to take it, this function may return
index 426d37bbc029ab8a2ae7e5d8eaed7b90dbc9f04c..3644d0e7aaa78ca61cfea8eb7b08e22cec7cb8d0 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_timedwrlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Mac OS X 10.5, FreeBSD 5.2.1, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 9, Cygwin, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Mac OS X 10.5, FreeBSD 5.2.1, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 9, Cygwin, mingw, MSVC 14.
 @end itemize
index 3e256b18e5cae8bfa4b7e7b1cb908f8acca6d69c..cbee92307c04c3eafc460bbe39ecf3cb591bba76 100644 (file)
@@ -4,18 +4,20 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_tryrdlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
 @item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
-@item
 This function prefers readers to writers (meaning, when this function is
 called on an rwlock that is already taken by one or more readers, and
 another writer is already waiting to take it, this function may return
index 51cc876a577617881996c56a14849d5e180a537c..e8f8b27e9c2157d9684182b51b918385a60ad736 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_trywrlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
 @end itemize
index ab4a5f0ee27b474197a8b746362f5d5c111056b2..95e2d7f913457b88707e8f34a1f48ba78f4513e7 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_unlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
 @end itemize
index a8a7f2d3f31cd1e070be1afd62db31d604a298b0..ba50b2df6e57a171480f54049ce2b0c4fd408a08 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14, Android 4.3.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
 @end itemize
index b8771f039f43d2bc36d28681fa0b9a0af2777de8..d32596e3d84cc474d9cbcefd11670fef4d43c48c 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlockattr_destroy.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
 @end itemize
index 80277295b195e8c022fe40c95cc214cafb795ade..9b2215ee8442d97ed3b5f9fb8dfc44d888bf5bfb 100644 (file)
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlockattr_init.html}
 
-Gnulib module: ---
+Gnulib module: pthread-rwlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Minix 3.1.8, mingw, MSVC 14.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Minix 3.1.8, HP-UX 11, mingw, MSVC 14.
 @end itemize
diff --git a/lib/pthread-rwlock.c b/lib/pthread-rwlock.c
new file mode 100644 (file)
index 0000000..4a1c125
--- /dev/null
@@ -0,0 +1,558 @@
+/* POSIX read-write locks.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2019.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <pthread.h>
+
+#if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
+# include "windows-timedrwlock.h"
+#else
+# include <errno.h>
+# include <limits.h>
+# include <sys/time.h>
+# include <time.h>
+#endif
+
+#if ((defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS) || !HAVE_PTHREAD_H
+
+int
+pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
+{
+  *attr = 0;
+  return 0;
+}
+
+int
+pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr _GL_UNUSED)
+{
+  return 0;
+}
+
+#endif
+
+#if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
+/* Use Windows threads.  */
+
+int
+pthread_rwlock_init (pthread_rwlock_t *lock,
+                     const pthread_rwlockattr_t *attr _GL_UNUSED)
+{
+  glwthread_timedrwlock_init (lock);
+  return 0;
+}
+
+int
+pthread_rwlock_rdlock (pthread_rwlock_t *lock)
+{
+  return glwthread_timedrwlock_rdlock (lock);
+}
+
+int
+pthread_rwlock_wrlock (pthread_rwlock_t *lock)
+{
+  return glwthread_timedrwlock_wrlock (lock);
+}
+
+int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
+{
+  return glwthread_timedrwlock_tryrdlock (lock);
+}
+
+int
+pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
+{
+  return glwthread_timedrwlock_trywrlock (lock);
+}
+
+int
+pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime)
+{
+  return glwthread_timedrwlock_timedrdlock (lock, abstime);
+}
+
+int
+pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime)
+{
+  return glwthread_timedrwlock_timedwrlock (lock, abstime);
+}
+
+int
+pthread_rwlock_unlock (pthread_rwlock_t *lock)
+{
+  return glwthread_timedrwlock_unlock (lock);
+}
+
+int
+pthread_rwlock_destroy (pthread_rwlock_t *lock)
+{
+  return glwthread_timedrwlock_destroy (lock);
+}
+
+#elif HAVE_PTHREAD_H
+/* Provide workarounds for POSIX threads.  */
+
+# if PTHREAD_RWLOCK_UNIMPLEMENTED
+
+int
+pthread_rwlock_init (pthread_rwlock_t *lock,
+                     const pthread_rwlockattr_t *attr _GL_UNUSED)
+{
+  int err;
+
+  err = pthread_mutex_init (&lock->lock, NULL);
+  if (err != 0)
+    return err;
+  err = pthread_cond_init (&lock->waiting_readers, NULL);
+  if (err != 0)
+    return err;
+  err = pthread_cond_init (&lock->waiting_writers, NULL);
+  if (err != 0)
+    return err;
+  lock->waiting_writers_count = 0;
+  lock->runcount = 0;
+  return 0;
+}
+
+int
+pthread_rwlock_rdlock (pthread_rwlock_t *lock)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  /* Test whether only readers are currently running, and whether the runcount
+     field will not overflow, and whether no writer is waiting.  The latter
+     condition is because POSIX recommends that "write locks shall take
+     precedence over read locks", to avoid "writer starvation".  */
+  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
+    {
+      /* This thread has to wait for a while.  Enqueue it among the
+         waiting_readers.  */
+      err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
+      if (err != 0)
+        {
+          pthread_mutex_unlock (&lock->lock);
+          return err;
+        }
+    }
+  lock->runcount++;
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_wrlock (pthread_rwlock_t *lock)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  /* Test whether no readers or writers are currently running.  */
+  while (!(lock->runcount == 0))
+    {
+      /* This thread has to wait for a while.  Enqueue it among the
+         waiting_writers.  */
+      lock->waiting_writers_count++;
+      err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
+      if (err != 0)
+        {
+          lock->waiting_writers_count--;
+          pthread_mutex_unlock (&lock->lock);
+          return err;
+        }
+      lock->waiting_writers_count--;
+    }
+  lock->runcount--; /* runcount becomes -1 */
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  /* Test whether only readers are currently running, and whether the runcount
+     field will not overflow, and whether no writer is waiting.  The latter
+     condition is because POSIX recommends that "write locks shall take
+     precedence over read locks", to avoid "writer starvation".  */
+  if (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
+    {
+      /* This thread would have to wait for a while.  Return instead.  */
+      pthread_mutex_unlock (&lock->lock);
+      return EBUSY;
+    }
+  lock->runcount++;
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  /* Test whether no readers or writers are currently running.  */
+  if (!(lock->runcount == 0))
+    {
+      /* This thread would have to wait for a while.  Return instead.  */
+      pthread_mutex_unlock (&lock->lock);
+      return EBUSY;
+    }
+  lock->runcount--; /* runcount becomes -1 */
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  /* Test whether only readers are currently running, and whether the runcount
+     field will not overflow, and whether no writer is waiting.  The latter
+     condition is because POSIX recommends that "write locks shall take
+     precedence over read locks", to avoid "writer starvation".  */
+  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
+    {
+      /* This thread has to wait for a while.  Enqueue it among the
+         waiting_readers.  */
+      err = pthread_cond_timedwait (&lock->waiting_readers, &lock->lock,
+                                    abstime);
+      if (err != 0)
+        {
+          pthread_mutex_unlock (&lock->lock);
+          return err;
+        }
+    }
+  lock->runcount++;
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  /* Test whether no readers or writers are currently running.  */
+  while (!(lock->runcount == 0))
+    {
+      /* This thread has to wait for a while.  Enqueue it among the
+         waiting_writers.  */
+      lock->waiting_writers_count++;
+      err = pthread_cond_timedwait (&lock->waiting_writers, &lock->lock,
+                                    abstime);
+      if (err != 0)
+        {
+          lock->waiting_writers_count--;
+          pthread_mutex_unlock (&lock->lock);
+          return err;
+        }
+      lock->waiting_writers_count--;
+    }
+  lock->runcount--; /* runcount becomes -1 */
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_unlock (pthread_rwlock_t *lock)
+{
+  int err;
+
+  err = pthread_mutex_lock (&lock->lock);
+  if (err != 0)
+    return err;
+  if (lock->runcount < 0)
+    {
+      /* Drop a writer lock.  */
+      if (!(lock->runcount == -1))
+        {
+          pthread_mutex_unlock (&lock->lock);
+          return EINVAL;
+        }
+      lock->runcount = 0;
+    }
+  else
+    {
+      /* Drop a reader lock.  */
+      if (!(lock->runcount > 0))
+        {
+          pthread_mutex_unlock (&lock->lock);
+          return EINVAL;
+        }
+      lock->runcount--;
+    }
+  if (lock->runcount == 0)
+    {
+      /* POSIX recommends that "write locks shall take precedence over read
+         locks", to avoid "writer starvation".  */
+      if (lock->waiting_writers_count > 0)
+        {
+          /* Wake up one of the waiting writers.  */
+          err = pthread_cond_signal (&lock->waiting_writers);
+          if (err != 0)
+            {
+              pthread_mutex_unlock (&lock->lock);
+              return err;
+            }
+        }
+      else
+        {
+          /* Wake up all waiting readers.  */
+          err = pthread_cond_broadcast (&lock->waiting_readers);
+          if (err != 0)
+            {
+              pthread_mutex_unlock (&lock->lock);
+              return err;
+            }
+        }
+    }
+  return pthread_mutex_unlock (&lock->lock);
+}
+
+int
+pthread_rwlock_destroy (pthread_rwlock_t *lock)
+{
+  int err;
+
+  err = pthread_mutex_destroy (&lock->lock);
+  if (err != 0)
+    return err;
+  err = pthread_cond_destroy (&lock->waiting_readers);
+  if (err != 0)
+    return err;
+  err = pthread_cond_destroy (&lock->waiting_writers);
+  if (err != 0)
+    return err;
+  return 0;
+}
+
+# elif PTHREAD_RWLOCK_LACKS_TIMEOUT
+
+int
+pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime)
+{
+  /* Poll the lock's state in regular intervals.  Ugh.  */
+  for (;;)
+    {
+      int err;
+      struct timeval currtime;
+      unsigned long remaining;
+      struct timespec duration;
+
+      err = pthread_rwlock_tryrdlock (lock);
+      if (err != EBUSY)
+        return err;
+
+      gettimeofday (&currtime, NULL);
+
+      if (currtime.tv_sec > abstime->tv_sec)
+        remaining = 0;
+      else
+        {
+          unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+          remaining = seconds * 1000000000;
+          if (remaining / 1000000000 != seconds) /* overflow? */
+            remaining = ULONG_MAX;
+          else
+            {
+              long nanoseconds =
+                abstime->tv_nsec - currtime.tv_usec * 1000;
+              if (nanoseconds >= 0)
+                {
+                  remaining += nanoseconds;
+                  if (remaining < nanoseconds) /* overflow? */
+                    remaining = ULONG_MAX;
+                }
+              else
+                {
+                  if (remaining >= - nanoseconds)
+                    remaining -= (- nanoseconds);
+                  else
+                    remaining = 0;
+                }
+            }
+        }
+      if (remaining == 0)
+        return ETIMEDOUT;
+
+      /* Sleep 1 ms.  */
+      duration.tv_sec = 0;
+      duration.tv_nsec = 1000000;
+      if (duration.tv_nsec > remaining)
+        duration.tv_nsec = remaining;
+      nanosleep (&duration, NULL);
+    }
+}
+
+int
+pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime)
+{
+  /* Poll the lock's state in regular intervals.  Ugh.  */
+  for (;;)
+    {
+      int err;
+      struct timeval currtime;
+      unsigned long remaining;
+      struct timespec duration;
+
+      err = pthread_rwlock_trywrlock (lock);
+      if (err != EBUSY)
+        return err;
+
+      gettimeofday (&currtime, NULL);
+
+      if (currtime.tv_sec > abstime->tv_sec)
+        remaining = 0;
+      else
+        {
+          unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+          remaining = seconds * 1000000000;
+          if (remaining / 1000000000 != seconds) /* overflow? */
+            remaining = ULONG_MAX;
+          else
+            {
+              long nanoseconds =
+                abstime->tv_nsec - currtime.tv_usec * 1000;
+              if (nanoseconds >= 0)
+                {
+                  remaining += nanoseconds;
+                  if (remaining < nanoseconds) /* overflow? */
+                    remaining = ULONG_MAX;
+                }
+              else
+                {
+                  if (remaining >= - nanoseconds)
+                    remaining -= (- nanoseconds);
+                  else
+                    remaining = 0;
+                }
+            }
+        }
+      if (remaining == 0)
+        return ETIMEDOUT;
+
+      /* Sleep 1 ms.  */
+      duration.tv_sec = 0;
+      duration.tv_nsec = 1000000;
+      if (duration.tv_nsec > remaining)
+        duration.tv_nsec = remaining;
+      nanosleep (&duration, NULL);
+    }
+}
+
+# endif
+
+#else
+/* Provide a dummy implementation for single-threaded applications.  */
+
+/* The pthread_rwlock_t is an 'int', representing the number of readers running,
+   or -1 when a writer runs.  */
+
+int
+pthread_rwlock_init (pthread_rwlock_t *lock,
+                     const pthread_rwlockattr_t *attr _GL_UNUSED)
+{
+  *lock = 0;
+  return 0;
+}
+
+int
+pthread_rwlock_rdlock (pthread_rwlock_t *lock)
+{
+  if (*lock < 0)
+    return EDEADLK;
+  (*lock)++;
+  return 0;
+}
+
+int
+pthread_rwlock_wrlock (pthread_rwlock_t *lock)
+{
+  if (*lock != 0)
+    return EDEADLK;
+  *lock = -1;
+  return 0;
+}
+
+int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
+{
+  return pthread_rwlock_rdlock (lock);
+}
+
+int
+pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
+{
+  return pthread_rwlock_wrlock (lock);
+}
+
+int
+pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime _GL_UNUSED)
+{
+  return pthread_rwlock_rdlock (lock);
+}
+
+int
+pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
+                            const struct timespec *abstime _GL_UNUSED)
+{
+  return pthread_rwlock_wrlock (lock);
+}
+
+int
+pthread_rwlock_unlock (pthread_rwlock_t *lock)
+{
+  if (*lock == 0)
+    return EPERM;
+  if (*lock < 0)
+    *lock = 0;
+  else /* *lock > 0 */
+    (*lock)--;
+  return 0;
+}
+
+int
+pthread_rwlock_destroy (pthread_rwlock_t *lock)
+{
+  if (*lock)
+    return EBUSY;
+  return 0;
+}
+
+#endif
diff --git a/m4/pthread-rwlock.m4 b/m4/pthread-rwlock.m4
new file mode 100644 (file)
index 0000000..ee5953c
--- /dev/null
@@ -0,0 +1,91 @@
+# pthread-rwlock.m4 serial 1
+dnl Copyright (C) 2019 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_PTHREAD_RWLOCK],
+[
+  AC_REQUIRE([gl_PTHREAD_H])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  if { case "$host_os" in mingw*) true;; *) false;; esac; } \
+     && test $gl_threads_api = windows; then
+    dnl Choose function names that don't conflict with the mingw-w64 winpthreads
+    dnl library.
+    REPLACE_PTHREAD_RWLOCK_INIT=1
+    REPLACE_PTHREAD_RWLOCKATTR_INIT=1
+    REPLACE_PTHREAD_RWLOCKATTR_DESTROY=1
+    REPLACE_PTHREAD_RWLOCK_RDLOCK=1
+    REPLACE_PTHREAD_RWLOCK_WRLOCK=1
+    REPLACE_PTHREAD_RWLOCK_TRYRDLOCK=1
+    REPLACE_PTHREAD_RWLOCK_TRYWRLOCK=1
+    REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK=1
+    REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK=1
+    REPLACE_PTHREAD_RWLOCK_UNLOCK=1
+    REPLACE_PTHREAD_RWLOCK_DESTROY=1
+  else
+    if test $HAVE_PTHREAD_H = 0; then
+      HAVE_PTHREAD_RWLOCK_INIT=0
+      HAVE_PTHREAD_RWLOCKATTR_INIT=0
+      HAVE_PTHREAD_RWLOCKATTR_DESTROY=0
+      HAVE_PTHREAD_RWLOCK_RDLOCK=0
+      HAVE_PTHREAD_RWLOCK_WRLOCK=0
+      HAVE_PTHREAD_RWLOCK_TRYRDLOCK=0
+      HAVE_PTHREAD_RWLOCK_TRYWRLOCK=0
+      HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK=0
+      HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK=0
+      HAVE_PTHREAD_RWLOCK_UNLOCK=0
+      HAVE_PTHREAD_RWLOCK_DESTROY=0
+    else
+      dnl On Android 4.3, the pthread_rwlock_* functions are declared in
+      dnl <pthread.h> but don't exist in libc.
+      AC_CACHE_CHECK([for pthread_rwlock_init],
+        [gl_cv_func_pthread_rwlock_init],
+        [saved_LIBS="$LIBS"
+         LIBS="$LIBS $LIBMULTITHREAD"
+         AC_LINK_IFELSE(
+           [AC_LANG_SOURCE(
+              [[extern
+                #ifdef __cplusplus
+                "C"
+                #endif
+                int pthread_rwlock_init (void);
+                int main ()
+                {
+                  return pthread_rwlock_init ();
+                }
+              ]])],
+           [gl_cv_func_pthread_rwlock_init=yes],
+           [gl_cv_func_pthread_rwlock_init=no])
+         LIBS="$saved_LIBS"
+        ])
+      if test $gl_cv_func_pthread_rwlock_init = no; then
+        REPLACE_PTHREAD_RWLOCK_INIT=1
+        REPLACE_PTHREAD_RWLOCKATTR_INIT=1
+        REPLACE_PTHREAD_RWLOCKATTR_DESTROY=1
+        REPLACE_PTHREAD_RWLOCK_RDLOCK=1
+        REPLACE_PTHREAD_RWLOCK_WRLOCK=1
+        REPLACE_PTHREAD_RWLOCK_TRYRDLOCK=1
+        REPLACE_PTHREAD_RWLOCK_TRYWRLOCK=1
+        REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK=1
+        REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK=1
+        REPLACE_PTHREAD_RWLOCK_UNLOCK=1
+        REPLACE_PTHREAD_RWLOCK_DESTROY=1
+        AC_DEFINE([PTHREAD_RWLOCK_UNIMPLEMENTED], [1],
+          [Define if all pthread_rwlock* functions don't exist.])
+      else
+        dnl On Mac OS X 10.5, FreeBSD 5.2.1, OpenBSD 3.8, AIX 5.1, HP-UX 11,
+        dnl IRIX 6.5, Solaris 9, Cygwin, the pthread_rwlock_timed*lock functions
+        dnl don't exist, although the other pthread_rwlock* functions exist.
+        AC_CHECK_DECL([pthread_rwlock_timedrdlock], ,
+          [HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK=0
+           HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK=0
+           AC_DEFINE([PTHREAD_RWLOCK_LACKS_TIMEOUT], [1],
+             [Define if the functions pthread_rwlock_timedrdlock and pthread_rwlock_timedwrlock don't exist.])
+          ],
+          [[#include <pthread.h>]])
+      fi
+    fi
+  fi
+])
diff --git a/modules/pthread-rwlock b/modules/pthread-rwlock
new file mode 100644 (file)
index 0000000..cb30e44
--- /dev/null
@@ -0,0 +1,33 @@
+Description:
+POSIX read-write locks.
+
+Files:
+lib/pthread-rwlock.c
+m4/pthread-rwlock.m4
+
+Depends-on:
+pthread-h
+pthread-cond        [test $gl_threads_api = posix && test $REPLACE_PTHREAD_RWLOCK_INIT = 1]
+nanosleep           [test $gl_threads_api = posix && test $HAVE_PTHREAD_RWLOCK_INIT = 1 && test $HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK = 0]
+windows-timedrwlock [test $gl_threads_api = windows]
+
+configure.ac:
+gl_PTHREAD_RWLOCK
+if test $HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK = 0 || test $REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK = 1; then
+  AC_LIBOBJ([pthread-rwlock])
+fi
+gl_PTHREAD_MODULE_INDICATOR([pthread-rwlock])
+
+Makefile.am:
+
+Include:
+<pthread.h>
+
+Link:
+$(LIBTHREAD) or $(LIBMULTITHREAD)
+
+License:
+LGPLv2+
+
+Maintainer:
+all