1 /* Copyright (C) 2015-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 /* This tests that with a reader-preferring rwlock, all readers are woken if
19    one reader "steals" lock ownership from a blocked writer.  */
20 
21 #include <errno.h>
22 #include <pthread.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <semaphore.h>
26 #include <unistd.h>
27 
28 /* If we strictly prefer writers over readers, a program must not expect
29    that, in the presence of concurrent writers, one reader will also acquire
30    the lock when another reader has already done so.  Thus, use the
31    default rwlock type that does not strictly prefer writers.  */
32 static pthread_rwlock_t r = PTHREAD_RWLOCK_INITIALIZER;
33 
34 static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
35 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
36 
37 /* Avoid using glibc-internal atomic operations.  */
38 static sem_t stop;
39 static int consumer_stop = 0;
40 
41 static void *
writer(void * arg)42 writer (void *arg)
43 {
44   int s;
45 
46   do
47     {
48       if (pthread_rwlock_wrlock (&r) != 0)
49 	{
50 	  puts ("wrlock failed");
51 	  exit (EXIT_FAILURE);
52 	}
53       if (pthread_rwlock_unlock (&r) != 0)
54 	{
55 	  puts ("unlock failed");
56 	  exit (EXIT_FAILURE);
57 	}
58       sem_getvalue (&stop, &s);
59     }
60   while (s == 0);
61   return NULL;
62 }
63 
64 static void *
reader_producer(void * arg)65 reader_producer (void *arg)
66 {
67   int s;
68 
69   do
70     {
71       if (pthread_rwlock_rdlock (&r) != 0)
72 	{
73 	  puts ("rdlock reader failed");
74 	  exit (EXIT_FAILURE);
75 	}
76 
77       sem_getvalue (&stop, &s);
78 
79       pthread_mutex_lock (&m);
80       if (s != 0)
81 	consumer_stop = 1;
82       pthread_cond_signal (&cv);
83       pthread_mutex_unlock (&m);
84 
85       if (pthread_rwlock_unlock (&r) != 0)
86 	{
87 	  puts ("unlock reader failed");
88 	  exit (EXIT_FAILURE);
89 	}
90     }
91   while (s == 0);
92   puts ("producer finished");
93   return NULL;
94 }
95 
96 static void *
reader_consumer(void * arg)97 reader_consumer (void *arg)
98 {
99   int s;
100 
101   do
102     {
103       if (pthread_rwlock_rdlock (&r) != 0)
104 	{
105 	  puts ("rdlock reader failed");
106 	  exit (EXIT_FAILURE);
107 	}
108 
109       pthread_mutex_lock (&m);
110       s = consumer_stop;
111       if (s == 0)
112 	pthread_cond_wait (&cv, &m);
113       pthread_mutex_unlock (&m);
114 
115       if (pthread_rwlock_unlock (&r) != 0)
116 	{
117 	  puts ("unlock reader failed");
118 	  exit (EXIT_FAILURE);
119 	}
120     }
121   while (s == 0);
122     puts ("consumer finished");
123   return NULL;
124 }
125 
126 
127 static int
do_test(void)128 do_test (void)
129 {
130   pthread_t w1, w2, rp, rc;
131 
132   if (pthread_create (&w1, NULL, writer, NULL) != 0)
133     {
134       puts ("create failed");
135       return 1;
136     }
137   if (pthread_create (&w2, NULL, writer, NULL) != 0)
138     {
139       puts ("create failed");
140       return 1;
141     }
142   if (pthread_create (&rc, NULL, reader_consumer, NULL) != 0)
143     {
144       puts ("create failed");
145       return 1;
146     }
147   if (pthread_create (&rp, NULL, reader_producer, NULL) != 0)
148     {
149       puts ("create failed");
150       return 1;
151     }
152 
153   sleep (2);
154   sem_post (&stop);
155 
156   if (pthread_join (w1, NULL) != 0)
157     {
158       puts ("w1 join failed");
159       return 1;
160     }
161   if (pthread_join (w2, NULL) != 0)
162     {
163       puts ("w2 join failed");
164       return 1;
165     }
166   if (pthread_join (rp, NULL) != 0)
167     {
168       puts ("reader_producer join failed");
169       return 1;
170     }
171   if (pthread_join (rc, NULL) != 0)
172     {
173       puts ("reader_consumer join failed");
174       return 1;
175     }
176 
177   return 0;
178 }
179 
180 
181 #define TEST_FUNCTION do_test ()
182 #include "../test-skeleton.c"
183