1 /* Verify that pthread_[gs]etattr_default_np work correctly.
2 
3    Copyright (C) 2013-2022 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19 
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdbool.h>
27 
28 #define RETURN_IF_FAIL(f, ...) \
29   ({									      \
30     int ret = f (__VA_ARGS__);						      \
31     if (ret != 0)							      \
32       {									      \
33 	printf ("%s:%d: %s returned %d (errno = %d)\n", __FILE__, __LINE__,   \
34 		#f, ret, errno);					      \
35 	return ret;							      \
36       }									      \
37   })
38 
39 static int (*verify_result) (pthread_attr_t *);
40 static size_t stacksize = 1024 * 1024;
41 static size_t guardsize;
42 static bool do_join = true;
43 static int running = 0;
44 static int detach_failed = 0;
45 static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
46 static pthread_cond_t c = PTHREAD_COND_INITIALIZER;
47 
48 static void *
thr(void * unused)49 thr (void *unused __attribute__ ((unused)))
50 {
51   pthread_attr_t attr;
52   int ret;
53 
54   memset (&attr, 0xab, sizeof attr);
55   /* To verify that the pthread_setattr_default_np worked.  */
56   if ((ret = pthread_getattr_default_np (&attr)) != 0)
57     {
58       printf ("pthread_getattr_default_np failed: %s\n", strerror (ret));
59       goto out;
60     }
61 
62   if ((ret = (*verify_result) (&attr)) != 0)
63     goto out;
64 
65   memset (&attr, 0xab, sizeof attr);
66   /* To verify that the attributes actually got applied.  */
67   if ((ret = pthread_getattr_np (pthread_self (), &attr)) != 0)
68     {
69       printf ("pthread_getattr_default_np failed: %s\n", strerror (ret));
70       goto out;
71     }
72 
73   ret = (*verify_result) (&attr);
74 
75 out:
76   if (!do_join)
77     {
78       pthread_mutex_lock (&m);
79       running--;
80       pthread_cond_signal (&c);
81       pthread_mutex_unlock (&m);
82 
83       detach_failed |= ret;
84     }
85 
86   return (void *) (uintptr_t) ret;
87 }
88 
89 static int
run_threads(const pthread_attr_t * attr)90 run_threads (const pthread_attr_t *attr)
91 {
92   pthread_t t;
93   void *tret = NULL;
94 
95   RETURN_IF_FAIL (pthread_setattr_default_np, attr);
96 
97   /* Run twice to ensure that the attributes do not get overwritten in the
98      first run somehow.  */
99   for (int i = 0; i < 2; i++)
100     {
101       RETURN_IF_FAIL (pthread_create, &t, NULL, thr, NULL);
102       if (do_join)
103 	RETURN_IF_FAIL (pthread_join, t, &tret);
104       else
105 	{
106 	  pthread_mutex_lock (&m);
107 	  running++;
108 	  pthread_mutex_unlock (&m);
109 	}
110 
111       if (tret != NULL)
112 	{
113 	  puts ("Thread failed");
114 	  return 1;
115 	}
116     }
117 
118   /* Stay in sync for detached threads and get their status.  */
119   while (!do_join)
120     {
121       pthread_mutex_lock (&m);
122       if (running == 0)
123 	{
124 	  pthread_mutex_unlock (&m);
125 	  break;
126 	}
127       pthread_cond_wait (&c, &m);
128       pthread_mutex_unlock (&m);
129     }
130 
131   return 0;
132 }
133 
134 static int
verify_detach_result(pthread_attr_t * attr)135 verify_detach_result (pthread_attr_t *attr)
136 {
137   int state;
138 
139   RETURN_IF_FAIL (pthread_attr_getdetachstate, attr, &state);
140 
141   if (state != PTHREAD_CREATE_DETACHED)
142     {
143       puts ("failed to set detach state");
144       return 1;
145     }
146 
147   return 0;
148 }
149 
150 static int
do_detach_test(void)151 do_detach_test (void)
152 {
153   pthread_attr_t attr;
154 
155   do_join = false;
156   RETURN_IF_FAIL (pthread_attr_init, &attr);
157   RETURN_IF_FAIL (pthread_attr_setdetachstate, &attr, PTHREAD_CREATE_DETACHED);
158 
159   RETURN_IF_FAIL (run_threads, &attr);
160   return detach_failed;
161 }
162 
163 static int
verify_affinity_result(pthread_attr_t * attr)164 verify_affinity_result (pthread_attr_t *attr)
165 {
166   cpu_set_t cpuset;
167 
168   RETURN_IF_FAIL (pthread_attr_getaffinity_np, attr, sizeof (cpuset), &cpuset);
169   if (!CPU_ISSET (0, &cpuset))
170     {
171       puts ("failed to set cpu affinity");
172       return 1;
173     }
174 
175   return 0;
176 }
177 
178 static int
do_affinity_test(void)179 do_affinity_test (void)
180 {
181   pthread_attr_t attr;
182 
183   RETURN_IF_FAIL (pthread_attr_init, &attr);
184 
185   /* Processor affinity.  Like scheduling policy, this could fail if the user
186      does not have the necessary privileges.  So we only spew a warning if
187      pthread_create fails with EPERM.  A computer has at least one CPU.  */
188   cpu_set_t cpuset;
189   CPU_ZERO (&cpuset);
190   CPU_SET (0, &cpuset);
191   RETURN_IF_FAIL (pthread_attr_setaffinity_np, &attr, sizeof (cpuset), &cpuset);
192 
193   int ret = run_threads (&attr);
194 
195   if (ret == EPERM)
196     {
197       printf ("Skipping CPU Affinity test: %s\n", strerror (ret));
198       return 0;
199     }
200   else if (ret != 0)
201     return ret;
202 
203   return 0;
204 }
205 
206 static int
verify_sched_result(pthread_attr_t * attr)207 verify_sched_result (pthread_attr_t *attr)
208 {
209   int inherited, policy;
210   struct sched_param param;
211 
212   RETURN_IF_FAIL (pthread_attr_getinheritsched, attr, &inherited);
213   if (inherited != PTHREAD_EXPLICIT_SCHED)
214     {
215       puts ("failed to set EXPLICIT_SCHED (%d != %d)");
216       return 1;
217     }
218 
219   RETURN_IF_FAIL (pthread_attr_getschedpolicy, attr, &policy);
220   if (policy != SCHED_RR)
221     {
222       printf ("failed to set SCHED_RR (%d != %d)\n", policy, SCHED_RR);
223       return 1;
224     }
225 
226   RETURN_IF_FAIL (pthread_attr_getschedparam, attr, &param);
227   if (param.sched_priority != 42)
228     {
229       printf ("failed to set sched_priority (%d != %d)\n",
230 	      param.sched_priority, 42);
231       return 1;
232     }
233 
234   return 0;
235 }
236 
237 static int
do_sched_test(void)238 do_sched_test (void)
239 {
240   pthread_attr_t attr;
241 
242   RETURN_IF_FAIL (pthread_attr_init, &attr);
243 
244   /* Scheduling policy.  Note that we don't always test these since it's
245      possible that the user the tests run as don't have the appropriate
246      privileges.  */
247   RETURN_IF_FAIL (pthread_attr_setinheritsched, &attr, PTHREAD_EXPLICIT_SCHED);
248   RETURN_IF_FAIL (pthread_attr_setschedpolicy, &attr, SCHED_RR);
249 
250   struct sched_param param;
251   param.sched_priority = 42;
252   RETURN_IF_FAIL (pthread_attr_setschedparam, &attr, &param);
253 
254   int ret = run_threads (&attr);
255 
256   if (ret == EPERM)
257     {
258       printf ("Skipping Scheduler Attributes test: %s\n", strerror (ret));
259       return 0;
260     }
261   else if (ret != 0)
262     return ret;
263 
264   return 0;
265 }
266 
267 static int
verify_guardsize_result(pthread_attr_t * attr)268 verify_guardsize_result (pthread_attr_t *attr)
269 {
270   size_t guard;
271 
272   RETURN_IF_FAIL (pthread_attr_getguardsize, attr, &guard);
273 
274   if (guardsize != guard)
275     {
276       printf ("failed to set guardsize (%zu, %zu)\n", guardsize, guard);
277       return 1;
278     }
279 
280   return 0;
281 }
282 
283 static int
do_guardsize_test(void)284 do_guardsize_test (void)
285 {
286   long int pagesize = sysconf (_SC_PAGESIZE);
287   pthread_attr_t attr;
288 
289   if (pagesize < 0)
290     {
291       printf ("sysconf failed: %s\n", strerror (errno));
292       return 1;
293     }
294 
295   RETURN_IF_FAIL (pthread_getattr_default_np, &attr);
296 
297   /* Increase default guardsize by a page.  */
298   RETURN_IF_FAIL (pthread_attr_getguardsize, &attr, &guardsize);
299   guardsize += pagesize;
300   RETURN_IF_FAIL (pthread_attr_setguardsize, &attr, guardsize);
301   RETURN_IF_FAIL (run_threads, &attr);
302 
303   return 0;
304 }
305 
306 static int
verify_stacksize_result(pthread_attr_t * attr)307 verify_stacksize_result (pthread_attr_t *attr)
308 {
309   size_t stack;
310 
311   RETURN_IF_FAIL (pthread_attr_getstacksize, attr, &stack);
312 
313   if (stacksize != stack)
314     {
315       printf ("failed to set default stacksize (%zu, %zu)\n", stacksize, stack);
316       return 1;
317     }
318 
319   return 0;
320 }
321 
322 static int
do_stacksize_test(void)323 do_stacksize_test (void)
324 {
325   long int pagesize = sysconf (_SC_PAGESIZE);
326   pthread_attr_t attr;
327 
328   if (pagesize < 0)
329     {
330       printf ("sysconf failed: %s\n", strerror (errno));
331       return 1;
332     }
333 
334   /* Perturb the size by a page so that we're not aligned on the 64K boundary.
335      pthread_create does this perturbation on x86 to avoid causing the 64k
336      aliasing conflict.  We want to prevent pthread_create from doing that
337      since it is not consistent for all architectures.  */
338   stacksize += pagesize;
339 
340   RETURN_IF_FAIL (pthread_attr_init, &attr);
341 
342   /* Run twice to ensure that we don't give a false positive.  */
343   RETURN_IF_FAIL (pthread_attr_setstacksize, &attr, stacksize);
344   RETURN_IF_FAIL (run_threads, &attr);
345   stacksize *= 2;
346   RETURN_IF_FAIL (pthread_attr_setstacksize, &attr, stacksize);
347   RETURN_IF_FAIL (run_threads, &attr);
348   return 0;
349 }
350 
351 /* We test each attribute separately because sched and affinity tests may need
352    additional user privileges that may not be available during the test run.
353    Each attribute test is a set of two functions, viz. a function to set the
354    default attribute (do_foo_test) and another to verify its result
355    (verify_foo_result).  Each test spawns a thread and checks (1) if the
356    attribute values were applied correctly and (2) if the change in the default
357    value reflected.  */
358 static int
do_test(void)359 do_test (void)
360 {
361   puts ("stacksize test");
362   verify_result = verify_stacksize_result;
363   RETURN_IF_FAIL (do_stacksize_test);
364 
365   puts ("guardsize test");
366   verify_result = verify_guardsize_result;
367   RETURN_IF_FAIL (do_guardsize_test);
368 
369   puts ("sched test");
370   verify_result = verify_sched_result;
371   RETURN_IF_FAIL (do_sched_test);
372 
373   puts ("affinity test");
374   verify_result = verify_affinity_result;
375   RETURN_IF_FAIL (do_affinity_test);
376 
377   puts ("detach test");
378   verify_result = verify_detach_result;
379   RETURN_IF_FAIL (do_detach_test);
380 
381   return 0;
382 }
383 
384 #define TEST_FUNCTION do_test ()
385 #include "../test-skeleton.c"
386