1 | /* Test program for timedout read/write lock functions. |
2 | Copyright (C) 2000-2022 Free Software Foundation, Inc. |
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 License as |
6 | published by the Free Software Foundation; either version 2.1 of the |
7 | 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; see the file COPYING.LIB. If |
16 | not, see <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <error.h> |
20 | #include <pthread.h> |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <time.h> |
24 | #include <unistd.h> |
25 | #include <sys/time.h> |
26 | #include <support/check.h> |
27 | #include <support/timespec.h> |
28 | #include <support/xthread.h> |
29 | |
30 | |
31 | #define NWRITERS 15 |
32 | #define WRITETRIES 10 |
33 | #define NREADERS 15 |
34 | #define READTRIES 15 |
35 | |
36 | static const struct timespec timeout = { 0,1000000 }; |
37 | static const struct timespec delay = { 0, 1000000 }; |
38 | |
39 | #ifndef KIND |
40 | # define KIND PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP |
41 | #endif |
42 | |
43 | /* A bogus clock value that tells the tests to use pthread_rwlock_timedrdlock |
44 | and pthread_rwlock_timedwrlock rather than pthread_rwlock_clockrdlock and |
45 | pthread_rwlock_clockwrlock. */ |
46 | #define CLOCK_USE_TIMEDLOCK (-1) |
47 | |
48 | static pthread_rwlock_t lock; |
49 | |
50 | struct thread_args |
51 | { |
52 | int nr; |
53 | clockid_t clockid; |
54 | const char *fnname; |
55 | }; |
56 | |
57 | static void * |
58 | writer_thread (void *arg) |
59 | { |
60 | struct thread_args *args = arg; |
61 | const int nr = args->nr; |
62 | const clockid_t clockid = args->clockid; |
63 | const clockid_t clockid_for_get = |
64 | (clockid == CLOCK_USE_TIMEDLOCK) ? CLOCK_REALTIME : clockid; |
65 | const char *fnname = args->fnname; |
66 | |
67 | struct timespec ts; |
68 | int n; |
69 | |
70 | for (n = 0; n < WRITETRIES; ++n) |
71 | { |
72 | int e; |
73 | do |
74 | { |
75 | xclock_gettime (clock: clockid_for_get, ts: &ts); |
76 | |
77 | ts = timespec_add (ts, timeout); |
78 | ts = timespec_add (ts, timeout); |
79 | |
80 | printf (format: "writer thread %d tries again\n" , nr); |
81 | |
82 | e = (clockid == CLOCK_USE_TIMEDLOCK) |
83 | ? pthread_rwlock_timedwrlock (rwlock: &lock, abstime: &ts) |
84 | : pthread_rwlock_clockwrlock (rwlock: &lock, clockid: clockid, abstime: &ts); |
85 | if (e != 0 && e != ETIMEDOUT) |
86 | FAIL_EXIT1 ("%swrlock failed" , fnname); |
87 | } |
88 | while (e == ETIMEDOUT); |
89 | |
90 | printf (format: "writer thread %d succeeded\n" , nr); |
91 | |
92 | nanosleep (requested_time: &delay, NULL); |
93 | |
94 | if (pthread_rwlock_unlock (rwlock: &lock) != 0) |
95 | FAIL_EXIT1 ("unlock for writer failed" ); |
96 | |
97 | printf (format: "writer thread %d released\n" , nr); |
98 | } |
99 | |
100 | return NULL; |
101 | } |
102 | |
103 | |
104 | static void * |
105 | reader_thread (void *arg) |
106 | { |
107 | struct thread_args *args = arg; |
108 | const int nr = args->nr; |
109 | const clockid_t clockid = args->clockid; |
110 | const clockid_t clockid_for_get = |
111 | (clockid == CLOCK_USE_TIMEDLOCK) ? CLOCK_REALTIME : clockid; |
112 | const char *fnname = args->fnname; |
113 | |
114 | struct timespec ts; |
115 | int n; |
116 | |
117 | for (n = 0; n < READTRIES; ++n) |
118 | { |
119 | int e; |
120 | do |
121 | { |
122 | xclock_gettime (clock: clockid_for_get, ts: &ts); |
123 | |
124 | ts = timespec_add (ts, timeout); |
125 | |
126 | printf (format: "reader thread %d tries again\n" , nr); |
127 | |
128 | if (clockid == CLOCK_USE_TIMEDLOCK) |
129 | e = pthread_rwlock_timedrdlock (rwlock: &lock, abstime: &ts); |
130 | else |
131 | e = pthread_rwlock_clockrdlock (rwlock: &lock, clockid: clockid, abstime: &ts); |
132 | if (e != 0 && e != ETIMEDOUT) |
133 | FAIL_EXIT1 ("%srdlock failed" , fnname); |
134 | } |
135 | while (e == ETIMEDOUT); |
136 | |
137 | printf (format: "reader thread %d succeeded\n" , nr); |
138 | |
139 | nanosleep (requested_time: &delay, NULL); |
140 | |
141 | if (pthread_rwlock_unlock (rwlock: &lock) != 0) |
142 | FAIL_EXIT1 ("unlock for reader failed" ); |
143 | |
144 | printf (format: "reader thread %d released\n" , nr); |
145 | } |
146 | |
147 | return NULL; |
148 | } |
149 | |
150 | |
151 | static int |
152 | do_test_clock (clockid_t clockid, const char *fnname) |
153 | { |
154 | pthread_t thwr[NWRITERS]; |
155 | pthread_t thrd[NREADERS]; |
156 | int n; |
157 | pthread_rwlockattr_t a; |
158 | |
159 | if (pthread_rwlockattr_init (attr: &a) != 0) |
160 | FAIL_EXIT1 ("rwlockattr_t failed" ); |
161 | |
162 | if (pthread_rwlockattr_setkind_np (attr: &a, KIND) != 0) |
163 | FAIL_EXIT1 ("rwlockattr_setkind failed" ); |
164 | |
165 | if (pthread_rwlock_init (rwlock: &lock, attr: &a) != 0) |
166 | FAIL_EXIT1 ("rwlock_init failed" ); |
167 | |
168 | /* Make standard error the same as standard output. */ |
169 | dup2 (fd: 1, fd2: 2); |
170 | |
171 | /* Make sure we see all message, even those on stdout. */ |
172 | setvbuf (stdout, NULL, _IONBF, n: 0); |
173 | |
174 | struct thread_args wargs[NWRITERS]; |
175 | for (n = 0; n < NWRITERS; ++n) { |
176 | wargs[n].nr = n; |
177 | wargs[n].clockid = clockid; |
178 | wargs[n].fnname = fnname; |
179 | thwr[n] = xpthread_create (NULL, thread_func: writer_thread, closure: &wargs[n]); |
180 | } |
181 | |
182 | struct thread_args rargs[NREADERS]; |
183 | for (n = 0; n < NREADERS; ++n) { |
184 | rargs[n].nr = n; |
185 | rargs[n].clockid = clockid; |
186 | rargs[n].fnname = fnname; |
187 | thrd[n] = xpthread_create (NULL, thread_func: reader_thread, closure: &rargs[n]); |
188 | } |
189 | |
190 | /* Wait for all the threads. */ |
191 | for (n = 0; n < NWRITERS; ++n) |
192 | xpthread_join (thr: thwr[n]); |
193 | for (n = 0; n < NREADERS; ++n) |
194 | xpthread_join (thr: thrd[n]); |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static int |
200 | do_test (void) |
201 | { |
202 | do_test_clock (CLOCK_USE_TIMEDLOCK, fnname: "timed" ); |
203 | do_test_clock (CLOCK_REALTIME, fnname: "clock(realtime)" ); |
204 | do_test_clock (CLOCK_MONOTONIC, fnname: "clock(monotonic)" ); |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | #define TIMEOUT 30 |
210 | #include <support/test-driver.c> |
211 | |