1 /* Copyright (C) 2002-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 #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
do_test(void)30 do_test (void)
31 {
32 size_t ps = sysconf (_SC_PAGESIZE);
33 char tmpfname[] = "/tmp/tst-rwlock12.XXXXXX";
34 char data[ps];
35 void *mem;
36 int fd;
37 pthread_mutex_t *m;
38 pthread_mutexattr_t ma;
39 pthread_rwlock_t *r;
40 pthread_rwlockattr_t ra;
41 pid_t pid;
42 int status = 0;
43
44 fd = mkstemp (tmpfname);
45 if (fd == -1)
46 {
47 printf ("cannot open temporary file: %m\n");
48 return 1;
49 }
50
51 /* Make sure it is always removed. */
52 unlink (tmpfname);
53
54 /* Create one page of data. */
55 memset (data, '\0', ps);
56
57 /* Write the data to the file. */
58 if (write (fd, data, ps) != (ssize_t) ps)
59 {
60 puts ("short write");
61 return 1;
62 }
63
64 mem = mmap (NULL, ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
65 if (mem == MAP_FAILED)
66 {
67 printf ("mmap failed: %m\n");
68 return 1;
69 }
70
71 r = (pthread_rwlock_t *) (((uintptr_t) mem + __alignof (pthread_rwlock_t))
72 & ~(__alignof (pthread_rwlock_t) - 1));
73 /* The following assumes alignment for a mutex is at least as high
74 as that for a rwlock. Which is true in our case. */
75 m = (pthread_mutex_t *) (r + 1);
76
77 if (pthread_rwlockattr_init (&ra) != 0)
78 {
79 puts ("rwlockattr_init failed");
80 return 1;
81 }
82
83 if (pthread_rwlockattr_setpshared (&ra, PTHREAD_PROCESS_SHARED) != 0)
84 {
85 puts ("rwlockattr_setpshared failed");
86 return 1;
87 }
88
89 if (pthread_rwlock_init (r, &ra) != 0)
90 {
91 puts ("rwlock_init failed");
92 return 1;
93 }
94
95 if (pthread_mutexattr_init (&ma) != 0)
96 {
97 puts ("rwlockattr_init failed");
98 return 1;
99 }
100
101 if (pthread_mutexattr_setpshared (&ma, PTHREAD_PROCESS_SHARED) != 0)
102 {
103 puts ("mutexattr_setpshared failed");
104 return 1;
105 }
106
107 if (pthread_mutex_init (m, &ma) != 0)
108 {
109 puts ("mutex_init failed");
110 return 1;
111 }
112
113 /* Lock the mutex. */
114 if (pthread_mutex_lock (m) != 0)
115 {
116 puts ("mutex_lock failed");
117 return 1;
118 }
119
120 puts ("going to fork now");
121 pid = fork ();
122 if (pid == -1)
123 {
124 puts ("fork failed");
125 return 1;
126 }
127 else if (pid == 0)
128 {
129 /* Lock the mutex. */
130 if (pthread_mutex_lock (m) != 0)
131 {
132 puts ("child: mutex_lock failed");
133 return 1;
134 }
135
136 /* Try to get the rwlock. */
137 if (pthread_rwlock_trywrlock (r) == 0)
138 {
139 puts ("rwlock_trywrlock succeeded");
140 return 1;
141 }
142
143 /* Try again. */
144 struct timespec ts = { .tv_sec = 0, .tv_nsec = 500000000 };
145 int e = pthread_rwlock_timedwrlock (r, &ts);
146 if (e == 0)
147 {
148 puts ("rwlock_timedwrlock succeeded");
149 return 1;
150 }
151 if (e != ETIMEDOUT)
152 {
153 puts ("rwlock_timedwrlock didn't return ETIMEDOUT");
154 status = 1;
155 }
156
157 if (pthread_rwlock_tryrdlock (r) == 0)
158 {
159 puts ("rwlock_tryrdlock succeeded");
160 return 1;
161 }
162
163 e = pthread_rwlock_timedrdlock (r, &ts);
164 if (e == 0)
165 {
166 puts ("rwlock_timedrdlock succeeded");
167 return 1;
168 }
169 if (e != ETIMEDOUT)
170 {
171 puts ("rwlock_timedrdlock didn't return ETIMEDOUT");
172 status = 1;
173 }
174 }
175 else
176 {
177 /* Lock the rwlock for writing. */
178 if (pthread_rwlock_wrlock (r) != 0)
179 {
180 puts ("rwlock_wrlock failed");
181 kill (pid, SIGTERM);
182 return 1;
183 }
184
185 /* Allow the child to run. */
186 if (pthread_mutex_unlock (m) != 0)
187 {
188 puts ("mutex_unlock failed");
189 kill (pid, SIGTERM);
190 return 1;
191 }
192
193 /* Just wait for the child. */
194 if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
195 {
196 puts ("waitpid failed");
197 kill (pid, SIGTERM);
198 return 1;
199 }
200 }
201
202 return status;
203 }
204
205 #define TEST_FUNCTION do_test ()
206 #include "../test-skeleton.c"
207