aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2020-02-09 20:07:51 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2020-02-09 20:07:51 +0000
commitd5ec049d44f019deaeed8adc0bd10819e5569e93 (patch)
tree26b03705007a5b239c5239124f8c9381dd83001a /sysdeps
parent28c30a6232aa9a54783c146590498a061fc0112a (diff)
downloadglibc-d5ec049d44f019deaeed8adc0bd10819e5569e93.tar
glibc-d5ec049d44f019deaeed8adc0bd10819e5569e93.tar.gz
glibc-d5ec049d44f019deaeed8adc0bd10819e5569e93.tar.bz2
glibc-d5ec049d44f019deaeed8adc0bd10819e5569e93.zip
pthread: Move some rwlock tests from nptl to sysdeps/pthread
So they can be checked with htl too.
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/pthread/Makefile2
-rw-r--r--sysdeps/pthread/tst-rwlock-tryrdlock-stall.c355
-rw-r--r--sysdeps/pthread/tst-rwlock-trywrlock-stall.c108
-rw-r--r--sysdeps/pthread/tst-rwlock1.c116
-rw-r--r--sysdeps/pthread/tst-rwlock13.c70
-rw-r--r--sysdeps/pthread/tst-rwlock16.c182
-rw-r--r--sysdeps/pthread/tst-rwlock4.c189
-rw-r--r--sysdeps/pthread/tst-rwlock5.c84
8 files changed, 1106 insertions, 0 deletions
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
index 6b49e33bdc..cceaa91837 100644
--- a/sysdeps/pthread/Makefile
+++ b/sysdeps/pthread/Makefile
@@ -52,6 +52,8 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
tst-cond-except \
tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex6 tst-mutex10 \
tst-once1 tst-once2 tst-once3 tst-once4 \
+ tst-rwlock1 tst-rwlock4 tst-rwlock5 tst-rwlock13 tst-rwlock16 \
+ tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
tst-spin1 tst-spin2 tst-spin3 tst-spin4
tests += tst-oncex3 tst-oncex4
diff --git a/sysdeps/pthread/tst-rwlock-tryrdlock-stall.c b/sysdeps/pthread/tst-rwlock-tryrdlock-stall.c
new file mode 100644
index 0000000000..bb2a64f301
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock-tryrdlock-stall.c
@@ -0,0 +1,355 @@
+/* Bug 23844: Test for pthread_rwlock_tryrdlock stalls.
+ Copyright (C) 2019-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* For a full analysis see comment:
+ https://sourceware.org/bugzilla/show_bug.cgi?id=23844#c14
+
+ Provided here for reference:
+
+ --- Analysis of pthread_rwlock_tryrdlock() stall ---
+ A read lock begins to execute.
+
+ In __pthread_rwlock_rdlock_full:
+
+ We can attempt a read lock, but find that the lock is
+ in a write phase (PTHREAD_RWLOCK_WRPHASE, or WP-bit
+ is set), and the lock is held by a primary writer
+ (PTHREAD_RWLOCK_WRLOCKED is set). In this case we must
+ wait for explicit hand over from the writer to us or
+ one of the other waiters. The read lock threads are
+ about to execute:
+
+ 341 r = (atomic_fetch_add_acquire (&rwlock->__data.__readers,
+ 342 (1 << PTHREAD_RWLOCK_READER_SHIFT))
+ 343 + (1 << PTHREAD_RWLOCK_READER_SHIFT));
+
+ An unlock beings to execute.
+
+ Then in __pthread_rwlock_wrunlock:
+
+ 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
+ ...
+ 549 while (!atomic_compare_exchange_weak_release
+ 550 (&rwlock->__data.__readers, &r,
+ 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
+ 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
+ 553 : PTHREAD_RWLOCK_WRPHASE))))
+ 554 {
+ ...
+ 556 }
+
+ We clear PTHREAD_RWLOCK_WRLOCKED, and if there are
+ no readers so we leave the lock in PTHRAD_RWLOCK_WRPHASE.
+
+ Back in the read lock.
+
+ The read lock adjusts __readres as above.
+
+ 383 while ((r & PTHREAD_RWLOCK_WRPHASE) != 0
+ 384 && (r & PTHREAD_RWLOCK_WRLOCKED) == 0)
+ 385 {
+ ...
+ 390 if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r,
+ 391 r ^ PTHREAD_RWLOCK_WRPHASE))
+ 392 {
+
+ And then attemps to start the read phase.
+
+ Assume there happens to be a tryrdlock at this point, noting
+ that PTHREAD_RWLOCK_WRLOCKED is clear, and PTHREAD_RWLOCK_WRPHASE
+ is 1. So the try lock attemps to start the read phase.
+
+ In __pthread_rwlock_tryrdlock:
+
+ 44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
+ 45 {
+ ...
+ 49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
+ 50 && (rwlock->__data.__flags
+ 51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
+ 52 return EBUSY;
+ 53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
+ 54 }
+ ...
+ 89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
+ 90 &r, rnew));
+
+ And succeeds.
+
+ Back in the write unlock:
+
+ 557 if ((r >> PTHREAD_RWLOCK_READER_SHIFT) != 0)
+ 558 {
+ ...
+ 563 if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
+ 564 & PTHREAD_RWLOCK_FUTEX_USED) != 0)
+ 565 futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
+ 566 }
+
+ We note that PTHREAD_RWLOCK_FUTEX_USED is non-zero
+ and don't wake anyone. This is OK because we handed
+ over to the trylock. It will be the trylock's responsibility
+ to wake any waiters.
+
+ Back in the read lock:
+
+ The read lock fails to install PTHRAD_REWLOCK_WRPHASE as 0 because
+ the __readers value was adjusted by the trylock, and so it falls through
+ to waiting on the lock for explicit handover from either a new writer
+ or a new reader.
+
+ 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
+ 449 1 | PTHREAD_RWLOCK_FUTEX_USED,
+ 450 abstime, private);
+
+ We use PTHREAD_RWLOCK_FUTEX_USED to indicate the futex
+ is in use.
+
+ At this point we have readers waiting on the read lock
+ to unlock. The wrlock is done. The trylock is finishing
+ the installation of the read phase.
+
+ 92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
+ 93 {
+ ...
+ 105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
+ 106 }
+
+ The trylock does note that we were the one that
+ installed the read phase, but the comments are not
+ correct, the execution ordering above shows that
+ readers might indeed be waiting, and they are.
+
+ The atomic_store_relaxed throws away PTHREAD_RWLOCK_FUTEX_USED,
+ and the waiting reader is never worken becuase as noted
+ above it is conditional on the futex being used.
+
+ The solution is for the trylock thread to inspect
+ PTHREAD_RWLOCK_FUTEX_USED and wake the waiting readers.
+
+ --- Analysis of pthread_rwlock_trywrlock() stall ---
+
+ A write lock begins to execute, takes the write lock,
+ and then releases the lock...
+
+ In pthread_rwlock_wrunlock():
+
+ 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
+ ...
+ 549 while (!atomic_compare_exchange_weak_release
+ 550 (&rwlock->__data.__readers, &r,
+ 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
+ 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
+ 553 : PTHREAD_RWLOCK_WRPHASE))))
+ 554 {
+ ...
+ 556 }
+
+ ... leaving it in the write phase with zero readers
+ (the case where we leave the write phase in place
+ during a write unlock).
+
+ A write trylock begins to execute.
+
+ In __pthread_rwlock_trywrlock:
+
+ 40 while (((r & PTHREAD_RWLOCK_WRLOCKED) == 0)
+ 41 && (((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0)
+ 42 || (prefer_writer && ((r & PTHREAD_RWLOCK_WRPHASE) != 0))))
+ 43 {
+
+ The lock is not locked.
+
+ There are no readers.
+
+ 45 if (atomic_compare_exchange_weak_acquire (
+ 46 &rwlock->__data.__readers, &r,
+ 47 r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
+
+ We atomically install the write phase and we take the
+ exclusive write lock.
+
+ 48 {
+ 49 atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
+
+ We get this far.
+
+ A reader lock begins to execute.
+
+ In pthread_rwlock_rdlock:
+
+ 437 for (;;)
+ 438 {
+ 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
+ 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
+ 441 {
+ 442 int private = __pthread_rwlock_get_private (rwlock);
+ 443 if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
+ 444 && (!atomic_compare_exchange_weak_relaxed
+ 445 (&rwlock->__data.__wrphase_futex,
+ 446 &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED)))
+ 447 continue;
+ 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
+ 449 1 | PTHREAD_RWLOCK_FUTEX_USED,
+ 450 abstime, private);
+
+ We are in a write phase, so the while() on line 439 is true.
+
+ The value of wpf does not have PTHREAD_RWLOCK_FUTEX_USED set
+ since this is the first reader to lock.
+
+ The atomic operation sets wpf with PTHREAD_RELOCK_FUTEX_USED
+ on the expectation that this reader will be woken during
+ the handoff.
+
+ Back in pthread_rwlock_trywrlock:
+
+ 50 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
+ 51 atomic_store_relaxed (&rwlock->__data.__cur_writer,
+ 52 THREAD_GETMEM (THREAD_SELF, tid));
+ 53 return 0;
+ 54 }
+ ...
+ 57 }
+
+ We write 1 to __wrphase_futex discarding PTHREAD_RWLOCK_FUTEX_USED,
+ and so in the unlock we will not awaken the waiting reader.
+
+ The solution to this is to realize that if we did not start the write
+ phase we need not write 1 or any other value to __wrphase_futex.
+ This ensures that any readers (which saw __wrphase_futex != 0) can
+ set PTHREAD_RWLOCK_FUTEX_USED and this can be used at unlock to
+ wake them.
+
+ If we installed the write phase then all other readers are looping
+ here:
+
+ In __pthread_rwlock_rdlock_full:
+
+ 437 for (;;)
+ 438 {
+ 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
+ 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
+ 441 {
+ ...
+ 508 }
+
+ waiting for the write phase to be installed or removed before they
+ can begin waiting on __wrphase_futex (part of the algorithm), or
+ taking a concurrent read lock, and thus we can safely write 1 to
+ __wrphase_futex.
+
+ If we did not install the write phase then the readers may already
+ be waiting on the futex, the original writer wrote 1 to __wrphase_futex
+ as part of starting the write phase, and we cannot also write 1
+ without loosing the PTHREAD_RWLOCK_FUTEX_USED bit.
+
+ ---
+
+ Summary for the pthread_rwlock_tryrdlock() stall:
+
+ The stall is caused by pthread_rwlock_tryrdlock failing to check
+ that PTHREAD_RWLOCK_FUTEX_USED is set in the __wrphase_futex futex
+ and then waking the futex.
+
+ The fix for bug 23844 ensures that waiters on __wrphase_futex are
+ correctly woken. Before the fix the test stalls as readers can
+ wait forever on __wrphase_futex. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <support/xthread.h>
+#include <errno.h>
+
+/* We need only one lock to reproduce the issue. We will need multiple
+ threads to get the exact case where we have a read, try, and unlock
+ all interleaving to produce the case where the readers are waiting
+ and the try fails to wake them. */
+pthread_rwlock_t onelock;
+
+/* The number of threads is arbitrary but empirically chosen to have
+ enough threads that we see the condition where waiting readers are
+ not woken by a successful tryrdlock. */
+#define NTHREADS 32
+
+_Atomic int do_exit;
+
+void *
+run_loop (void *arg)
+{
+ int i = 0, ret;
+ while (!do_exit)
+ {
+ /* Arbitrarily choose if we are the writer or reader. Choose a
+ high enough ratio of readers to writers to make it likely
+ that readers block (and eventually are susceptable to
+ stalling).
+
+ If we are a writer, take the write lock, and then unlock.
+ If we are a reader, try the lock, then lock, then unlock. */
+ if ((i % 8) != 0)
+ xpthread_rwlock_wrlock (&onelock);
+ else
+ {
+ if ((ret = pthread_rwlock_tryrdlock (&onelock)) != 0)
+ {
+ if (ret == EBUSY)
+ xpthread_rwlock_rdlock (&onelock);
+ else
+ exit (EXIT_FAILURE);
+ }
+ }
+ /* Thread does some work and then unlocks. */
+ xpthread_rwlock_unlock (&onelock);
+ i++;
+ }
+ return NULL;
+}
+
+int
+do_test (void)
+{
+ int i;
+ pthread_t tids[NTHREADS];
+ xpthread_rwlock_init (&onelock, NULL);
+ for (i = 0; i < NTHREADS; i++)
+ tids[i] = xpthread_create (NULL, run_loop, NULL);
+ /* Run for some amount of time. Empirically speaking exercising
+ the stall via pthread_rwlock_tryrdlock is much harder, and on
+ a 3.5GHz 4 core x86_64 VM system it takes somewhere around
+ 20-200s to stall, approaching 100% stall past 200s. We can't
+ wait that long for a regression test so we just test for 20s,
+ and expect the stall to happen with a 5-10% chance (enough for
+ developers to see). */
+ sleep (20);
+ /* Then exit. */
+ printf ("INFO: Exiting...\n");
+ do_exit = 1;
+ /* If any readers stalled then we will timeout waiting for them. */
+ for (i = 0; i < NTHREADS; i++)
+ xpthread_join (tids[i]);
+ printf ("INFO: Done.\n");
+ xpthread_rwlock_destroy (&onelock);
+ printf ("PASS: No pthread_rwlock_tryrdlock stalls detected.\n");
+ return 0;
+}
+
+#define TIMEOUT 30
+#include <support/test-driver.c>
diff --git a/sysdeps/pthread/tst-rwlock-trywrlock-stall.c b/sysdeps/pthread/tst-rwlock-trywrlock-stall.c
new file mode 100644
index 0000000000..b4e60c5af3
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock-trywrlock-stall.c
@@ -0,0 +1,108 @@
+/* Bug 23844: Test for pthread_rwlock_trywrlock stalls.
+ Copyright (C) 2019-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c.
+
+ Summary for the pthread_rwlock_trywrlock() stall:
+
+ The stall is caused by pthread_rwlock_trywrlock setting
+ __wrphase_futex futex to 1 and loosing the
+ PTHREAD_RWLOCK_FUTEX_USED bit.
+
+ The fix for bug 23844 ensures that waiters on __wrphase_futex are
+ correctly woken. Before the fix the test stalls as readers can
+ wait forever on __wrphase_futex. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <support/xthread.h>
+#include <errno.h>
+
+/* We need only one lock to reproduce the issue. We will need multiple
+ threads to get the exact case where we have a read, try, and unlock
+ all interleaving to produce the case where the readers are waiting
+ and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a
+ subsequent unlock fails to wake them. */
+pthread_rwlock_t onelock;
+
+/* The number of threads is arbitrary but empirically chosen to have
+ enough threads that we see the condition where waiting readers are
+ not woken by a successful unlock. */
+#define NTHREADS 32
+
+_Atomic int do_exit;
+
+void *
+run_loop (void *arg)
+{
+ int i = 0, ret;
+ while (!do_exit)
+ {
+ /* Arbitrarily choose if we are the writer or reader. Choose a
+ high enough ratio of readers to writers to make it likely
+ that readers block (and eventually are susceptable to
+ stalling).
+
+ If we are a writer, take the write lock, and then unlock.
+ If we are a reader, try the lock, then lock, then unlock. */
+ if ((i % 8) != 0)
+ {
+ if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0)
+ {
+ if (ret == EBUSY)
+ xpthread_rwlock_wrlock (&onelock);
+ else
+ exit (EXIT_FAILURE);
+ }
+ }
+ else
+ xpthread_rwlock_rdlock (&onelock);
+ /* Thread does some work and then unlocks. */
+ xpthread_rwlock_unlock (&onelock);
+ i++;
+ }
+ return NULL;
+}
+
+int
+do_test (void)
+{
+ int i;
+ pthread_t tids[NTHREADS];
+ xpthread_rwlock_init (&onelock, NULL);
+ for (i = 0; i < NTHREADS; i++)
+ tids[i] = xpthread_create (NULL, run_loop, NULL);
+ /* Run for some amount of time. The pthread_rwlock_tryrwlock stall
+ is very easy to trigger and happens in seconds under the test
+ conditions. */
+ sleep (10);
+ /* Then exit. */
+ printf ("INFO: Exiting...\n");
+ do_exit = 1;
+ /* If any readers stalled then we will timeout waiting for them. */
+ for (i = 0; i < NTHREADS; i++)
+ xpthread_join (tids[i]);
+ printf ("INFO: Done.\n");
+ xpthread_rwlock_destroy (&onelock);
+ printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n");
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/pthread/tst-rwlock1.c b/sysdeps/pthread/tst-rwlock1.c
new file mode 100644
index 0000000000..d14d3036a2
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock1.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <stdio.h>
+
+
+static int
+do_test (void)
+{
+ pthread_rwlock_t r;
+
+ if (pthread_rwlock_init (&r, NULL) != 0)
+ {
+ puts ("rwlock_init failed");
+ return 1;
+ }
+ puts ("rwlock_init succeeded");
+
+ if (pthread_rwlock_rdlock (&r) != 0)
+ {
+ puts ("1st rwlock_rdlock failed");
+ return 1;
+ }
+ puts ("1st rwlock_rdlock succeeded");
+
+ if (pthread_rwlock_rdlock (&r) != 0)
+ {
+ puts ("2nd rwlock_rdlock failed");
+ return 1;
+ }
+ puts ("2nd rwlock_rdlock succeeded");
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("1st rwlock_unlock failed");
+ return 1;
+ }
+ puts ("1st rwlock_unlock succeeded");
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("2nd rwlock_unlock failed");
+ return 1;
+ }
+ puts ("2nd rwlock_unlock succeeded");
+
+ if (pthread_rwlock_wrlock (&r) != 0)
+ {
+ puts ("1st rwlock_wrlock failed");
+ return 1;
+ }
+ puts ("1st rwlock_wrlock succeeded");
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("3rd rwlock_unlock failed");
+ return 1;
+ }
+ puts ("3rd rwlock_unlock succeeded");
+
+ if (pthread_rwlock_wrlock (&r) != 0)
+ {
+ puts ("2nd rwlock_wrlock failed");
+ return 1;
+ }
+ puts ("2nd rwlock_wrlock succeeded");
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("4th rwlock_unlock failed");
+ return 1;
+ }
+ puts ("4th rwlock_unlock succeeded");
+
+ if (pthread_rwlock_rdlock (&r) != 0)
+ {
+ puts ("3rd rwlock_rdlock failed");
+ return 1;
+ }
+ puts ("3rd rwlock_rdlock succeeded");
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("5th rwlock_unlock failed");
+ return 1;
+ }
+ puts ("5th rwlock_unlock succeeded");
+
+ if (pthread_rwlock_destroy (&r) != 0)
+ {
+ puts ("rwlock_destroy failed");
+ return 1;
+ }
+ puts ("rwlock_destroy succeeded");
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/pthread/tst-rwlock13.c b/sysdeps/pthread/tst-rwlock13.c
new file mode 100644
index 0000000000..749ec4b621
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock13.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 2004-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+
+static int
+do_test (void)
+{
+ pthread_rwlock_t r;
+ int ret;
+
+ memset (&r, 0xaa, sizeof (r));
+ if ((ret = pthread_rwlock_init (&r, NULL)) != 0)
+ {
+ printf ("rwlock_init failed: %d\n", ret);
+ return 1;
+ }
+
+ if ((ret = pthread_rwlock_rdlock (&r)) != 0)
+ {
+ printf ("rwlock_rdlock failed: %d\n", ret);
+ return 1;
+ }
+
+ if ((ret = pthread_rwlock_unlock (&r)) != 0)
+ {
+ printf ("rwlock_unlock failed: %d\n", ret);
+ return 1;
+ }
+
+ if ((ret = pthread_rwlock_wrlock (&r)) != 0)
+ {
+ printf ("rwlock_wrlock failed: %d\n", ret);
+ return 1;
+ }
+
+ if ((ret = pthread_rwlock_unlock (&r)) != 0)
+ {
+ printf ("second rwlock_unlock failed: %d\n", ret);
+ return 1;
+ }
+
+ if ((ret = pthread_rwlock_destroy (&r)) != 0)
+ {
+ printf ("second rwlock_destroy failed: %d\n", ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/pthread/tst-rwlock16.c b/sysdeps/pthread/tst-rwlock16.c
new file mode 100644
index 0000000000..29720c9d8d
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock16.c
@@ -0,0 +1,182 @@
+/* Copyright (C) 2015-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* This tests that with a reader-preferring rwlock, all readers are woken if
+ one reader "steals" lock ownership from a blocked writer. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <semaphore.h>
+#include <unistd.h>
+
+/* If we strictly prefer writers over readers, a program must not expect
+ that, in the presence of concurrent writers, one reader will also acquire
+ the lock when another reader has already done so. Thus, use the
+ default rwlock type that does not strictly prefer writers. */
+static pthread_rwlock_t r = PTHREAD_RWLOCK_INITIALIZER;
+
+static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
+
+/* Avoid using glibc-internal atomic operations. */
+static sem_t stop;
+static int consumer_stop = 0;
+
+static void *
+writer (void *arg)
+{
+ int s;
+
+ do
+ {
+ if (pthread_rwlock_wrlock (&r) != 0)
+ {
+ puts ("wrlock failed");
+ exit (EXIT_FAILURE);
+ }
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("unlock failed");
+ exit (EXIT_FAILURE);
+ }
+ sem_getvalue (&stop, &s);
+ }
+ while (s == 0);
+ return NULL;
+}
+
+static void *
+reader_producer (void *arg)
+{
+ int s;
+
+ do
+ {
+ if (pthread_rwlock_rdlock (&r) != 0)
+ {
+ puts ("rdlock reader failed");
+ exit (EXIT_FAILURE);
+ }
+
+ sem_getvalue (&stop, &s);
+
+ pthread_mutex_lock (&m);
+ if (s != 0)
+ consumer_stop = 1;
+ pthread_cond_signal (&cv);
+ pthread_mutex_unlock (&m);
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("unlock reader failed");
+ exit (EXIT_FAILURE);
+ }
+ }
+ while (s == 0);
+ puts ("producer finished");
+ return NULL;
+}
+
+static void *
+reader_consumer (void *arg)
+{
+ int s;
+
+ do
+ {
+ if (pthread_rwlock_rdlock (&r) != 0)
+ {
+ puts ("rdlock reader failed");
+ exit (EXIT_FAILURE);
+ }
+
+ pthread_mutex_lock (&m);
+ s = consumer_stop;
+ if (s == 0)
+ pthread_cond_wait (&cv, &m);
+ pthread_mutex_unlock (&m);
+
+ if (pthread_rwlock_unlock (&r) != 0)
+ {
+ puts ("unlock reader failed");
+ exit (EXIT_FAILURE);
+ }
+ }
+ while (s == 0);
+ puts ("consumer finished");
+ return NULL;
+}
+
+
+static int
+do_test (void)
+{
+ pthread_t w1, w2, rp, rc;
+
+ if (pthread_create (&w1, NULL, writer, NULL) != 0)
+ {
+ puts ("create failed");
+ return 1;
+ }
+ if (pthread_create (&w2, NULL, writer, NULL) != 0)
+ {
+ puts ("create failed");
+ return 1;
+ }
+ if (pthread_create (&rc, NULL, reader_consumer, NULL) != 0)
+ {
+ puts ("create failed");
+ return 1;
+ }
+ if (pthread_create (&rp, NULL, reader_producer, NULL) != 0)
+ {
+ puts ("create failed");
+ return 1;
+ }
+
+ sleep (2);
+ sem_post (&stop);
+
+ if (pthread_join (w1, NULL) != 0)
+ {
+ puts ("w1 join failed");
+ return 1;
+ }
+ if (pthread_join (w2, NULL) != 0)
+ {
+ puts ("w2 join failed");
+ return 1;
+ }
+ if (pthread_join (rp, NULL) != 0)
+ {
+ puts ("reader_producer join failed");
+ return 1;
+ }
+ if (pthread_join (rc, NULL) != 0)
+ {
+ puts ("reader_consumer join failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/pthread/tst-rwlock4.c b/sysdeps/pthread/tst-rwlock4.c
new file mode 100644
index 0000000000..479107a5f2
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock4.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+
+static int
+do_test (void)
+{
+ size_t ps = sysconf (_SC_PAGESIZE);
+ char tmpfname[] = "/tmp/tst-rwlock4.XXXXXX";
+ char data[ps];
+ void *mem;
+ int fd;
+ pthread_rwlock_t *r;
+ pthread_rwlockattr_t a;
+ pid_t pid;
+ char *p;
+ int err;
+ int s;
+
+ fd = mkstemp (tmpfname);
+ if (fd == -1)
+ {
+ printf ("cannot open temporary file: %m\n");
+ return 1;
+ }
+
+ /* Make sure it is always removed. */
+ unlink (tmpfname);
+
+ /* Create one page of data. */
+ memset (data, '\0', ps);
+
+ /* Write the data to the file. */
+ if (write (fd, data, ps) != (ssize_t) ps)
+ {
+ puts ("short write");
+ return 1;
+ }
+
+ mem = mmap (NULL, ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ printf ("mmap failed: %m\n");
+ return 1;
+ }
+
+ r = (pthread_rwlock_t *) (((uintptr_t) mem + __alignof (pthread_rwlock_t))
+ & ~(__alignof (pthread_rwlock_t) - 1));
+ p = (char *) (r + 1);
+
+ if (pthread_rwlockattr_init (&a) != 0)
+ {
+ puts ("rwlockattr_init failed");
+ return 1;
+ }
+
+ if (pthread_rwlockattr_getpshared (&a, &s) != 0)
+ {
+ puts ("1st rwlockattr_getpshared failed");
+ return 1;
+ }
+
+ if (s != PTHREAD_PROCESS_PRIVATE)
+ {
+ puts ("default pshared value wrong");
+ return 1;
+ }
+
+ if (pthread_rwlockattr_setpshared (&a, PTHREAD_PROCESS_SHARED) != 0)
+ {
+ puts ("rwlockattr_setpshared failed");
+ return 1;
+ }
+
+ if (pthread_rwlockattr_getpshared (&a, &s) != 0)
+ {
+ puts ("2nd rwlockattr_getpshared failed");
+ return 1;
+ }
+
+ if (s != PTHREAD_PROCESS_SHARED)
+ {
+ puts ("pshared value after setpshared call wrong");
+ return 1;
+ }
+
+ if (pthread_rwlock_init (r, &a) != 0)
+ {
+ puts ("rwlock_init failed");
+ return 1;
+ }
+
+ if (pthread_rwlock_rdlock (r) != 0)
+ {
+ puts ("rwlock_rdlock failed");
+ return 1;
+ }
+
+ if (pthread_rwlockattr_destroy (&a) != 0)
+ {
+ puts ("rwlockattr_destroy failed");
+ return 1;
+ }
+
+ err = pthread_rwlock_trywrlock (r);
+ if (err == 0)
+ {
+ puts ("rwlock_trywrlock succeeded");
+ return 1;
+ }
+ else if (err != EBUSY)
+ {
+ puts ("rwlock_trywrlock didn't return EBUSY");
+ return 1;
+ }
+
+ *p = 0;
+
+ puts ("going to fork now");
+ pid = fork ();
+ if (pid == -1)
+ {
+ puts ("fork failed");
+ return 1;
+ }
+ else if (pid == 0)
+ {
+ /* Play some lock ping-pong. It's our turn to unlock first. */
+ if ((*p)++ != 0)
+ {
+ puts ("child: *p != 0");
+ return 1;
+ }
+
+ if (pthread_rwlock_unlock (r) != 0)
+ {
+ puts ("child: 1st rwlock_unlock failed");
+ return 1;
+ }
+
+ puts ("child done");
+ }
+ else
+ {
+ if (pthread_rwlock_wrlock (r) != 0)
+ {
+ puts ("parent: rwlock_wrlock failed");
+ return 1;
+ }
+
+ if (*p != 1)
+ {
+ puts ("*p != 1");
+ return 1;
+ }
+
+ puts ("parent done");
+ }
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/pthread/tst-rwlock5.c b/sysdeps/pthread/tst-rwlock5.c
new file mode 100644
index 0000000000..c589764196
--- /dev/null
+++ b/sysdeps/pthread/tst-rwlock5.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int do_test (void);
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+static pthread_rwlock_t r;
+
+
+static void *
+tf (void *arg)
+{
+ if (pthread_rwlock_wrlock (&r) == 0)
+ {
+ puts ("child: rwlock_wrlock succeeded");
+ exit (1);
+ }
+
+ puts ("child: rwlock_wrlock returned");
+
+ exit (1);
+}
+
+
+static int
+do_test (void)
+{
+ pthread_t th;
+
+ if (pthread_rwlock_init (&r, NULL) != 0)
+ {
+ puts ("rwlock_init failed");
+ return 1;
+ }
+
+ if (pthread_rwlock_wrlock (&r) != 0)
+ {
+ puts ("rwlock_wrlock failed");
+ return 1;
+ }
+
+ if (pthread_mutex_lock (&m) != 0)
+ {
+ puts ("mutex_lock failed");
+ return 1;
+ }
+
+ if (pthread_create (&th, NULL, tf, NULL) != 0)
+ {
+ puts ("create failed");
+ return 1;
+ }
+
+ delayed_exit (1);
+ /* This call should never return. */
+ xpthread_mutex_lock (&m);
+
+ puts ("2nd mutex_lock returned");
+ return 1;
+}