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
29static int
30do_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 (template: tmpfname);
45 if (fd == -1)
46 {
47 printf (format: "cannot open temporary file: %m\n");
48 return 1;
49 }
50
51 /* Make sure it is always removed. */
52 unlink (name: 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 (s: "short write");
61 return 1;
62 }
63
64 mem = mmap (NULL, len: ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd: fd, offset: 0);
65 if (mem == MAP_FAILED)
66 {
67 printf (format: "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 (attr: &ra) != 0)
78 {
79 puts (s: "rwlockattr_init failed");
80 return 1;
81 }
82
83 if (pthread_rwlockattr_setpshared (attr: &ra, PTHREAD_PROCESS_SHARED) != 0)
84 {
85 puts (s: "rwlockattr_setpshared failed");
86 return 1;
87 }
88
89 if (pthread_rwlock_init (rwlock: r, attr: &ra) != 0)
90 {
91 puts (s: "rwlock_init failed");
92 return 1;
93 }
94
95 if (pthread_mutexattr_init (attr: &ma) != 0)
96 {
97 puts (s: "rwlockattr_init failed");
98 return 1;
99 }
100
101 if (pthread_mutexattr_setpshared (attr: &ma, PTHREAD_PROCESS_SHARED) != 0)
102 {
103 puts (s: "mutexattr_setpshared failed");
104 return 1;
105 }
106
107 if (pthread_mutex_init (mutex: m, mutexattr: &ma) != 0)
108 {
109 puts (s: "mutex_init failed");
110 return 1;
111 }
112
113 /* Lock the mutex. */
114 if (pthread_mutex_lock (mutex: m) != 0)
115 {
116 puts (s: "mutex_lock failed");
117 return 1;
118 }
119
120 puts (s: "going to fork now");
121 pid = fork ();
122 if (pid == -1)
123 {
124 puts (s: "fork failed");
125 return 1;
126 }
127 else if (pid == 0)
128 {
129 /* Lock the mutex. */
130 if (pthread_mutex_lock (mutex: m) != 0)
131 {
132 puts (s: "child: mutex_lock failed");
133 return 1;
134 }
135
136 /* Try to get the rwlock. */
137 if (pthread_rwlock_trywrlock (rwlock: r) == 0)
138 {
139 puts (s: "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 (rwlock: r, abstime: &ts);
146 if (e == 0)
147 {
148 puts (s: "rwlock_timedwrlock succeeded");
149 return 1;
150 }
151 if (e != ETIMEDOUT)
152 {
153 puts (s: "rwlock_timedwrlock didn't return ETIMEDOUT");
154 status = 1;
155 }
156
157 if (pthread_rwlock_tryrdlock (rwlock: r) == 0)
158 {
159 puts (s: "rwlock_tryrdlock succeeded");
160 return 1;
161 }
162
163 e = pthread_rwlock_timedrdlock (rwlock: r, abstime: &ts);
164 if (e == 0)
165 {
166 puts (s: "rwlock_timedrdlock succeeded");
167 return 1;
168 }
169 if (e != ETIMEDOUT)
170 {
171 puts (s: "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 (rwlock: r) != 0)
179 {
180 puts (s: "rwlock_wrlock failed");
181 kill (pid: pid, SIGTERM);
182 return 1;
183 }
184
185 /* Allow the child to run. */
186 if (pthread_mutex_unlock (mutex: m) != 0)
187 {
188 puts (s: "mutex_unlock failed");
189 kill (pid: 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 (s: "waitpid failed");
197 kill (pid: 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

source code of glibc/sysdeps/pthread/tst-rwlock12.c