1 /* Bug 23844: Test for pthread_rwlock_trywrlock stalls.
2    Copyright (C) 2019-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c.
20 
21    Summary for the pthread_rwlock_trywrlock() stall:
22 
23    The stall is caused by pthread_rwlock_trywrlock setting
24    __wrphase_futex futex to 1 and loosing the
25    PTHREAD_RWLOCK_FUTEX_USED bit.
26 
27    The fix for bug 23844 ensures that waiters on __wrphase_futex are
28    correctly woken.  Before the fix the test stalls as readers can
29    wait forever on  __wrphase_futex.  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <pthread.h>
35 #include <support/xthread.h>
36 #include <errno.h>
37 
38 /* We need only one lock to reproduce the issue. We will need multiple
39    threads to get the exact case where we have a read, try, and unlock
40    all interleaving to produce the case where the readers are waiting
41    and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a
42    subsequent unlock fails to wake them.  */
43 pthread_rwlock_t onelock;
44 
45 /* The number of threads is arbitrary but empirically chosen to have
46    enough threads that we see the condition where waiting readers are
47    not woken by a successful unlock.  */
48 #define NTHREADS 32
49 
50 _Atomic int do_exit;
51 
52 void *
run_loop(void * arg)53 run_loop (void *arg)
54 {
55   int i = 0, ret;
56   while (!do_exit)
57     {
58       /* Arbitrarily choose if we are the writer or reader.  Choose a
59 	 high enough ratio of readers to writers to make it likely
60 	 that readers block (and eventually are susceptable to
61 	 stalling).
62 
63          If we are a writer, take the write lock, and then unlock.
64 	 If we are a reader, try the lock, then lock, then unlock.  */
65       if ((i % 8) != 0)
66 	{
67 	  if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0)
68 	    {
69 	      if (ret == EBUSY)
70 		xpthread_rwlock_wrlock (&onelock);
71 	      else
72 		exit (EXIT_FAILURE);
73 	    }
74 	}
75       else
76 	xpthread_rwlock_rdlock (&onelock);
77       /* Thread does some work and then unlocks.  */
78       xpthread_rwlock_unlock (&onelock);
79       i++;
80     }
81   return NULL;
82 }
83 
84 int
do_test(void)85 do_test (void)
86 {
87   int i;
88   pthread_t tids[NTHREADS];
89   xpthread_rwlock_init (&onelock, NULL);
90   for (i = 0; i < NTHREADS; i++)
91     tids[i] = xpthread_create (NULL, run_loop, NULL);
92   /* Run for some amount of time.  The pthread_rwlock_tryrwlock stall
93      is very easy to trigger and happens in seconds under the test
94      conditions.  */
95   sleep (10);
96   /* Then exit.  */
97   printf ("INFO: Exiting...\n");
98   do_exit = 1;
99   /* If any readers stalled then we will timeout waiting for them.  */
100   for (i = 0; i < NTHREADS; i++)
101     xpthread_join (tids[i]);
102   printf ("INFO: Done.\n");
103   xpthread_rwlock_destroy (&onelock);
104   printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n");
105   return 0;
106 }
107 
108 #include <support/test-driver.c>
109