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