1 | /* Copyright (C) 2002-2024 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 | #include <errno.h> |
19 | #include <pthread.h> |
20 | #include <stdint.h> |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <unistd.h> |
25 | #include <sys/mman.h> |
26 | #include <sys/wait.h> |
27 | |
28 | |
29 | static int |
30 | do_test (void) |
31 | { |
32 | size_t ps = sysconf (_SC_PAGESIZE); |
33 | char tmpfname[] = "/tmp/tst-mutex4.XXXXXX" ; |
34 | char data[ps]; |
35 | void *mem; |
36 | int fd; |
37 | pthread_mutex_t *m; |
38 | pthread_mutexattr_t a; |
39 | pid_t pid; |
40 | char *p; |
41 | int err; |
42 | int s; |
43 | pthread_barrier_t *b; |
44 | pthread_barrierattr_t ba; |
45 | |
46 | fd = mkstemp (template: tmpfname); |
47 | if (fd == -1) |
48 | { |
49 | printf (format: "cannot open temporary file: %m\n" ); |
50 | return 1; |
51 | } |
52 | |
53 | /* Make sure it is always removed. */ |
54 | unlink (name: tmpfname); |
55 | |
56 | /* Create one page of data. */ |
57 | memset (data, '\0', ps); |
58 | |
59 | /* Write the data to the file. */ |
60 | if (write (fd, data, ps) != (ssize_t) ps) |
61 | { |
62 | puts (s: "short write" ); |
63 | return 1; |
64 | } |
65 | |
66 | mem = mmap (NULL, len: ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd: fd, offset: 0); |
67 | if (mem == MAP_FAILED) |
68 | { |
69 | printf (format: "mmap failed: %m\n" ); |
70 | return 1; |
71 | } |
72 | |
73 | m = (pthread_mutex_t *) (((uintptr_t) mem + __alignof (pthread_mutex_t) - 1) |
74 | & ~(__alignof (pthread_mutex_t) - 1)); |
75 | b = (pthread_barrier_t *) (((uintptr_t) (m + 1) |
76 | + __alignof (pthread_barrier_t) - 1) |
77 | & ~(__alignof (pthread_barrier_t) - 1)); |
78 | p = (char *) (b + 1); |
79 | |
80 | if (pthread_mutexattr_init (attr: &a) != 0) |
81 | { |
82 | puts (s: "mutexattr_init failed" ); |
83 | return 1; |
84 | } |
85 | |
86 | if (pthread_mutexattr_getpshared (attr: &a, pshared: &s) != 0) |
87 | { |
88 | puts (s: "1st mutexattr_getpshared failed" ); |
89 | return 1; |
90 | } |
91 | |
92 | if (s != PTHREAD_PROCESS_PRIVATE) |
93 | { |
94 | puts (s: "default pshared value wrong" ); |
95 | return 1; |
96 | } |
97 | |
98 | if (pthread_mutexattr_setpshared (attr: &a, PTHREAD_PROCESS_SHARED) != 0) |
99 | { |
100 | puts (s: "mutexattr_setpshared failed" ); |
101 | return 1; |
102 | } |
103 | |
104 | if (pthread_mutexattr_getpshared (attr: &a, pshared: &s) != 0) |
105 | { |
106 | puts (s: "2nd mutexattr_getpshared failed" ); |
107 | return 1; |
108 | } |
109 | |
110 | if (s != PTHREAD_PROCESS_SHARED) |
111 | { |
112 | puts (s: "pshared value after setpshared call wrong" ); |
113 | return 1; |
114 | } |
115 | |
116 | #ifdef ENABLE_PI |
117 | if (pthread_mutexattr_setprotocol (attr: &a, protocol: PTHREAD_PRIO_INHERIT) != 0) |
118 | { |
119 | puts (s: "pthread_mutexattr_setprotocol failed" ); |
120 | return 1; |
121 | } |
122 | #endif |
123 | |
124 | if ((err = pthread_mutex_init (mutex: m, mutexattr: &a)) != 0) |
125 | { |
126 | #ifdef ENABLE_PI |
127 | if (err == ENOTSUP) |
128 | { |
129 | puts (s: "PI mutexes unsupported" ); |
130 | return 0; |
131 | } |
132 | #endif |
133 | puts (s: "mutex_init failed" ); |
134 | return 1; |
135 | } |
136 | |
137 | if (pthread_mutex_lock (mutex: m) != 0) |
138 | { |
139 | puts (s: "mutex_lock failed" ); |
140 | return 1; |
141 | } |
142 | |
143 | if (pthread_mutexattr_destroy (attr: &a) != 0) |
144 | { |
145 | puts (s: "mutexattr_destroy failed" ); |
146 | return 1; |
147 | } |
148 | |
149 | if (pthread_barrierattr_init (attr: &ba) != 0) |
150 | { |
151 | puts (s: "barrierattr_init failed" ); |
152 | return 1; |
153 | } |
154 | |
155 | if (pthread_barrierattr_setpshared (attr: &ba, PTHREAD_PROCESS_SHARED) != 0) |
156 | { |
157 | puts (s: "barrierattr_setpshared failed" ); |
158 | return 1; |
159 | } |
160 | |
161 | if (pthread_barrier_init (barrier: b, attr: &ba, count: 2) != 0) |
162 | { |
163 | puts (s: "barrier_init failed" ); |
164 | return 1; |
165 | } |
166 | |
167 | if (pthread_barrierattr_destroy (attr: &ba) != 0) |
168 | { |
169 | puts (s: "barrierattr_destroy failed" ); |
170 | return 1; |
171 | } |
172 | |
173 | err = pthread_mutex_trylock (mutex: m); |
174 | if (err == 0) |
175 | { |
176 | puts (s: "mutex_trylock succeeded" ); |
177 | return 1; |
178 | } |
179 | else if (err != EBUSY) |
180 | { |
181 | puts (s: "mutex_trylock didn't return EBUSY" ); |
182 | return 1; |
183 | } |
184 | |
185 | *p = 0; |
186 | |
187 | if (pthread_mutex_unlock (mutex: m) != 0) |
188 | { |
189 | puts (s: "parent: 1st mutex_unlock failed" ); |
190 | return 1; |
191 | } |
192 | |
193 | puts (s: "going to fork now" ); |
194 | pid = fork (); |
195 | if (pid == -1) |
196 | { |
197 | puts (s: "fork failed" ); |
198 | return 1; |
199 | } |
200 | else if (pid == 0) |
201 | { |
202 | if (pthread_mutex_lock (mutex: m) != 0) |
203 | { |
204 | puts (s: "child: mutex_lock failed" ); |
205 | return 1; |
206 | } |
207 | |
208 | int e = pthread_barrier_wait (barrier: b); |
209 | if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) |
210 | { |
211 | puts (s: "child: barrier_wait failed" ); |
212 | return 1; |
213 | } |
214 | |
215 | if ((*p)++ != 0) |
216 | { |
217 | puts (s: "child: *p != 0" ); |
218 | return 1; |
219 | } |
220 | |
221 | if (pthread_mutex_unlock (mutex: m) != 0) |
222 | { |
223 | puts (s: "child: mutex_unlock failed" ); |
224 | return 1; |
225 | } |
226 | |
227 | puts (s: "child done" ); |
228 | } |
229 | else |
230 | { |
231 | int e = pthread_barrier_wait (barrier: b); |
232 | if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) |
233 | { |
234 | puts (s: "parent: barrier_wait failed" ); |
235 | return 1; |
236 | } |
237 | |
238 | if (pthread_mutex_lock (mutex: m) != 0) |
239 | { |
240 | puts (s: "parent: 2nd mutex_lock failed" ); |
241 | return 1; |
242 | } |
243 | |
244 | if (*p != 1) |
245 | { |
246 | puts (s: "*p != 1" ); |
247 | return 1; |
248 | } |
249 | |
250 | if (pthread_mutex_unlock (mutex: m) != 0) |
251 | { |
252 | puts (s: "parent: 2nd mutex_unlock failed" ); |
253 | return 1; |
254 | } |
255 | |
256 | if (pthread_mutex_destroy (mutex: m) != 0) |
257 | { |
258 | puts (s: "mutex_destroy failed" ); |
259 | return 1; |
260 | } |
261 | |
262 | if (pthread_barrier_destroy (barrier: b) != 0) |
263 | { |
264 | puts (s: "barrier_destroy failed" ); |
265 | return 1; |
266 | } |
267 | |
268 | puts (s: "parent done" ); |
269 | } |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | #define TEST_FUNCTION do_test () |
275 | #include "../test-skeleton.c" |
276 | |