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