1 /*
2  * Copyright (c) 2004, Bull S.A..  All rights reserved.
3  * Created by: Sebastien Decugis
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  *
17  * This file is a helper file for the pthread_create tests
18  * It defines the following objects:
19  * scenarii: array of struct __scenario type.
20  * NSCENAR : macro giving the total # of scenarii
21  * scenar_init(): function to call before use the scenarii array.
22  * scenar_fini(): function to call after end of use of the scenarii array.
23  */
24 
25 static struct __scenario {
26     /*
27      * Object to hold the given configuration,
28      * and which will be used to create the threads
29      */
30     pthread_attr_t ta;
31 
32     /* General parameters */
33     /* 0 => joinable; 1 => detached */
34     int detached;
35 
36     /* Scheduling parameters */
37     /*
38      * 0 => sched policy is inherited;
39      * 1 => sched policy from the attr param
40      */
41     int explicitsched;
42     /* 0 => default; 1=> SCHED_FIFO; 2=> SCHED_RR */
43     int schedpolicy;
44     /*
45      * 0 => default sched param;
46      * 1 => max value for sched param;
47      * -1 => min value for sched param
48      */
49     int schedparam;
50     /*
51      * 0 => default contension scope;
52      * 1 => alternative contension scope
53      */
54     int altscope;
55 
56     /* Stack parameters */
57     /* 0 => system manages the stack; 1 => stack is provided */
58     int altstack;
59     /*
60      * 0 => default guardsize;
61      * 1=> guardsize is 0;
62      * 2=> guard is 1 page
63      *     -- this setting only affect system stacks (not user's).
64      */
65     int guard;
66     /*
67      * 0 => default stack size;
68      * 1 => stack size specified (min value)
69      *      -- ignored when stack is provided
70      */
71     int altsize;
72 
73     /* Additionnal information */
74     /* object description */
75     char *descr;
76     /* Stores the stack start when an alternate stack is required */
77     void *bottom;
78     /*
79      * This thread creation is expected to:
80      * 0 => succeed; 1 => fail; 2 => unknown
81      */
82     int result;
83     /*
84      * This semaphore is used to signal the end of
85      * the detached threads execution
86      */
87     sem_t sem;
88 } scenarii[] =
89 #define CASE(det, expl, scp, spa, sco, sta, gua, ssi, desc, res)    \
90 {                                   \
91     .detached = det,                        \
92     .explicitsched = expl,                      \
93     .schedpolicy = scp,                     \
94     .schedparam = spa,                      \
95     .altscope = sco,                        \
96     .altstack = sta,                        \
97     .guard = gua,                           \
98     .altsize = ssi,                         \
99     .descr = desc,                          \
100     .bottom = NULL,                         \
101     .result = res                           \
102 }
103 #define CASE_POS(det, expl, scp, spa, sco, sta, gua, ssi, desc)     \
104     CASE(det, expl, scp, spa, sco, sta, gua, ssi, desc, 0)
105 #define CASE_NEG(det, expl, scp, spa, sco, sta, gua, ssi, desc)     \
106     CASE(det, expl, scp, spa, sco, sta, gua, ssi, desc, 1)
107 #define CASE_UNK(det, expl, scp, spa, sco, sta, gua, ssi, desc)     \
108     CASE(det, expl, scp, spa, sco, sta, gua, ssi, desc, 2)
109 /*
110  * This array gives the different combinations of threads
111  * attributes for the testcases.
112  *
113  * Some combinations must be avoided.
114  * -> Do not have a detached thread use an alternative stack;
115  *     as we don't know when the thread terminates to free the stack memory
116  * -> ... (to be completed)
117  */
118 {
119     /* Unary tests */
120     CASE_POS(0, 0, 0, 0, 0, 0, 0, 0, "default"),
121         CASE_POS(1, 0, 0, 0, 0, 0, 0, 0, "detached"),
122         CASE_POS(0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched"),
123         CASE_UNK(0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy"),
124         CASE_UNK(0, 0, 2, 0, 0, 0, 0, 0, "RR Policy"),
125         CASE_UNK(0, 0, 0, 1, 0, 0, 0, 0, "Max sched param"),
126         CASE_UNK(0, 0, 0, -1, 0, 0, 0, 0, "Min sched param"),
127         CASE_POS(0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope"),
128         CASE_POS(0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack"),
129         CASE_POS(0, 0, 0, 0, 0, 0, 1, 0, "No guard size"),
130         CASE_UNK(0, 0, 0, 0, 0, 0, 2, 0, "1p guard size"),
131         CASE_POS(0, 0, 0, 0, 0, 0, 0, 1, "Min stack size"),
132         /* Stack play */
133         CASE_POS(0, 0, 0, 0, 0, 0, 1, 1, "Min stack size, no guard"),
134         CASE_UNK(0, 0, 0, 0, 0, 0, 2, 1, "Min stack size, 1p guard"),
135         CASE_POS(1, 0, 0, 0, 0, 1, 0, 0, "Detached, Alternative stack"),
136         CASE_POS(1, 0, 0, 0, 0, 0, 1, 1,
137              "Detached, Min stack size, no guard"), CASE_UNK(1, 0, 0, 0,
138                                      0, 0, 2, 1,
139                                      "Detached, Min stack size, 1p guard"),
140         /*
141          * Scheduling play
142          *   -- all results are unknown since it might depend on
143          *      the user priviledges
144          */
145 CASE_UNK(0, 1, 1, 1, 0, 0, 0, 0, "Explicit FIFO max param"),
146         CASE_UNK(0, 1, 2, 1, 0, 0, 0, 0,
147                  "Explicit RR max param"),
148         CASE_UNK(0, 1, 1, -1, 0, 0, 0, 0,
149                  "Explicit FIFO min param"),
150         CASE_UNK(0, 1, 2, -1, 0, 0, 0, 0,
151                  "Explicit RR min param"),
152         CASE_UNK(0, 1, 1, 1, 1, 0, 0, 0,
153                  "Explicit FIFO max param, alt scope"),
154         CASE_UNK(0, 1, 2, 1, 1, 0, 0, 0,
155                  "Explicit RR max param, alt scope"),
156         CASE_UNK(0, 1, 1, -1, 1, 0, 0, 0,
157                  "Explicit FIFO min param, alt scope"),
158         CASE_UNK(0, 1, 2, -1, 1, 0, 0, 0,
159                  "Explicit RR min param, alt scope"),
160         CASE_UNK(1, 1, 1, 1, 0, 0, 0, 0,
161                  "Detached, explicit FIFO max param"),
162         CASE_UNK(1, 1, 2, 1, 0, 0, 0, 0,
163                  "Detached, explicit RR max param"),
164         CASE_UNK(1, 1, 1, -1, 0, 0, 0, 0,
165                  "Detached, explicit FIFO min param"),
166         CASE_UNK(1, 1, 2, -1, 0, 0, 0, 0,
167                  "Detached, explicit RR min param"),
168         CASE_UNK(1, 1, 1, 1, 1, 0, 0, 0,
169                  "Detached, explicit FIFO max param,"
170                  " alt scope"), CASE_UNK(1, 1, 2, 1,
171                                      1,
172                                      0,
173                                      0,
174                                      0,
175                                      "Detached, explicit RR max param,"
176                                      " alt scope"),
177         CASE_UNK(1, 1, 1, -1, 1, 0, 0, 0,
178                  "Detached, explicit FIFO min param,"
179                  " alt scope"), CASE_UNK(1, 1, 2,
180                                      -1,
181                                      1,
182                                      0,
183                                      0,
184                                      0,
185                                      "Detached, explicit RR min param,"
186                                      " alt scope"),};
187 
188 #define NSCENAR (sizeof(scenarii) / sizeof(scenarii[0]))
189 
190 /*
191  * This function will initialize every pthread_attr_t object
192  * in the scenarii array
193  */
scenar_init(void)194 static void scenar_init(void)
195 {
196     int ret = 0;
197     unsigned int i;
198     int old;
199     long pagesize, minstacksize;
200     long tsa, tss, tps;
201 
202     pagesize = sysconf(_SC_PAGESIZE);
203     minstacksize = sysconf(_SC_THREAD_STACK_MIN);
204     tsa = sysconf(_SC_THREAD_ATTR_STACKADDR);
205     tss = sysconf(_SC_THREAD_ATTR_STACKSIZE);
206     tps = sysconf(_SC_THREAD_PRIORITY_SCHEDULING);
207 
208 #if VERBOSE > 0
209     output("System abilities:\n");
210     output(" TSA: %li\n", tsa);
211     output(" TSS: %li\n", tss);
212     output(" TPS: %li\n", tps);
213     output(" pagesize: %li\n", pagesize);
214     output(" min stack size: %li\n", minstacksize);
215 #endif
216 
217     if (minstacksize % pagesize)
218         UNTESTED("The min stack size is not a multiple"
219              " of the page size");
220 
221     for (i = 0; i < NSCENAR; i++) {
222 #if VERBOSE > 2
223         output("Initializing attribute for scenario %i: %s\n",
224                i, scenarii[i].descr);
225 #endif
226 
227         ret = pthread_attr_init(&scenarii[i].ta);
228         if (ret != 0)
229             UNRESOLVED(ret, "Failed to initialize a"
230                    " thread attribute object");
231 
232         /* Set the attributes according to the scenario */
233         if (scenarii[i].detached == 1) {
234             ret = pthread_attr_setdetachstate(&scenarii[i].ta,
235                               PTHREAD_CREATE_DETACHED);
236             if (ret != 0)
237                 UNRESOLVED(ret, "Unable to set detachstate");
238         } else {
239             ret =
240                 pthread_attr_getdetachstate(&scenarii[i].ta, &old);
241             if (ret != 0)
242                 UNRESOLVED(ret, "Unable to get detachstate"
243                        " from initialized attribute");
244             if (old != PTHREAD_CREATE_JOINABLE)
245                 FAILED("The default attribute is not"
246                        " PTHREAD_CREATE_JOINABLE");
247         }
248 #if VERBOSE > 4
249         output("Detach state was set successfully\n");
250 #endif
251 
252         /* Sched related attributes */
253         /*
254          * This routine is dependent on the Thread Execution
255          * Scheduling option
256          */
257         if (tps > 0) {
258             if (scenarii[i].explicitsched == 1)
259                 ret =
260                     pthread_attr_setinheritsched(&scenarii
261                                  [i].ta,
262                                  PTHREAD_EXPLICIT_SCHED);
263             else
264                 ret =
265                     pthread_attr_setinheritsched(&scenarii
266                                  [i].ta,
267                                  PTHREAD_INHERIT_SCHED);
268             if (ret != 0)
269                 UNRESOLVED(ret, "Unable to set inheritsched"
270                        " attribute");
271 #if VERBOSE > 4
272             output("inheritsched state was set successfully\n");
273 #endif
274         }
275 #if VERBOSE > 4
276         else
277             output("TPS unsupported => inheritsched parameter"
278                    " untouched\n");
279 #endif
280 
281         if (tps > 0) {
282             if (scenarii[i].schedpolicy == 1)
283                 ret =
284                     pthread_attr_setschedpolicy(&scenarii[i].ta,
285                                 SCHED_FIFO);
286             if (scenarii[i].schedpolicy == 2)
287                 ret =
288                     pthread_attr_setschedpolicy(&scenarii[i].ta,
289                                 SCHED_RR);
290             if (ret != 0)
291                 UNRESOLVED(ret, "Unable to set the"
292                        " sched policy");
293 #if VERBOSE > 4
294             if (scenarii[i].schedpolicy)
295                 output("Sched policy was set successfully\n");
296             else
297                 output("Sched policy untouched\n");
298 #endif
299         }
300 #if VERBOSE > 4
301         else
302             output("TPS unsupported => sched policy parameter"
303                    " untouched\n");
304 #endif
305 
306         if (scenarii[i].schedparam != 0) {
307             struct sched_param sp;
308 
309             ret =
310                 pthread_attr_getschedpolicy(&scenarii[i].ta, &old);
311             if (ret != 0)
312                 UNRESOLVED(ret, "Unable to get sched policy"
313                        " from attribute");
314 
315             if (scenarii[i].schedparam == 1)
316                 sp.sched_priority = sched_get_priority_max(old);
317             if (scenarii[i].schedparam == -1)
318                 sp.sched_priority = sched_get_priority_min(old);
319 
320             ret = pthread_attr_setschedparam(&scenarii[i].ta, &sp);
321             if (ret != 0)
322                 UNRESOLVED(ret,
323                        "Failed to set the sched param");
324 
325 #if VERBOSE > 4
326             output("Sched param was set successfully to %i\n",
327                    sp.sched_priority);
328         } else {
329             output("Sched param untouched\n");
330 #endif
331         }
332 
333         if (tps > 0) {
334             ret = pthread_attr_getscope(&scenarii[i].ta, &old);
335             if (ret != 0)
336                 UNRESOLVED(ret, "Failed to get contension"
337                        " scope from thread attribute");
338 
339             if (scenarii[i].altscope != 0) {
340                 if (old == PTHREAD_SCOPE_PROCESS)
341                     old = PTHREAD_SCOPE_SYSTEM;
342                 else
343                     old = PTHREAD_SCOPE_PROCESS;
344 
345                 ret =
346                     pthread_attr_setscope(&scenarii[i].ta, old);
347 
348 #if VERBOSE > 0
349                 if (ret != 0)
350                     output("WARNING: The TPS option is"
351                            " claimed to be supported but"
352                            " setscope fails\n");
353 #endif
354 
355 #if VERBOSE > 4
356                 output("Contension scope set to %s\n",
357                        old == PTHREAD_SCOPE_PROCESS ?
358                        "PTHREAD_SCOPE_PROCESS" :
359                        "PTHREAD_SCOPE_SYSTEM");
360             } else {
361                 output("Contension scope untouched (%s)\n",
362                        old == PTHREAD_SCOPE_PROCESS ?
363                        "PTHREAD_SCOPE_PROCESS" :
364                        "PTHREAD_SCOPE_SYSTEM");
365 #endif
366             }
367         }
368 #if VERBOSE > 4
369         else
370             output("TPS unsupported => sched contension scope"
371                    " parameter untouched\n");
372 #endif
373 
374         /* Stack related attributes */
375         /*
376          * This routine is dependent on the Thread Stack Address
377          * Attribute and Thread Stack Size Attribute options
378          */
379         if ((tss > 0) && (tsa > 0)) {
380             if (scenarii[i].altstack != 0) {
381                 /*
382                  * This is slightly more complicated.
383                  * We need to alloc a new stackand free
384                  * it upon test termination.
385                  * We will alloc with a simulated guardsize
386                  * of 1 pagesize */
387                 scenarii[i].bottom = malloc(minstacksize + pagesize);
388                 if (scenarii[i].bottom == NULL)
389                     UNRESOLVED(errno, "Unable to alloc"
390                            " enough memory for"
391                            " alternative stack");
392 
393                 ret = pthread_attr_setstack(&scenarii[i].ta,
394                                 scenarii[i].bottom,
395                                 minstacksize);
396                 if (ret != 0)
397                     UNRESOLVED(ret, "Failed to specify"
398                            " alternate stack");
399 
400 #if VERBOSE > 1
401                 output("Alternate stack created successfully."
402                        " Bottom=%p, Size=%i\n",
403                        scenarii[i].bottom, minstacksize);
404 #endif
405             }
406         }
407 #if VERBOSE > 4
408         else
409             output("TSA or TSS unsupported => "
410                    "No alternative stack\n");
411 #endif
412 
413 #ifndef WITHOUT_XOPEN
414         if (scenarii[i].guard != 0) {
415             if (scenarii[i].guard == 1)
416                 ret =
417                     pthread_attr_setguardsize(&scenarii[i].ta,
418                                   0);
419             if (scenarii[i].guard == 2)
420                 ret =
421                     pthread_attr_setguardsize(&scenarii[i].ta,
422                                   pagesize);
423             if (ret != 0)
424                 UNRESOLVED(ret, "Unable to set guard area"
425                        " size in thread stack");
426 #if VERBOSE > 4
427             output("Guard size set to %i\n",
428                    scenarii[i].guard == 1 ? 1 : pagesize);
429 #endif
430         }
431 #endif
432 
433         if (tss > 0) {
434             if (scenarii[i].altsize != 0) {
435                 ret = pthread_attr_setstacksize(&scenarii[i].ta,
436                                 minstacksize);
437                 if (ret != 0)
438                     UNRESOLVED(ret, "Unable to change"
439                            " stack size");
440 #if VERBOSE > 4
441                 output("Stack size set to %i (this is the "
442                        "min)\n", minstacksize);
443 #endif
444             }
445         }
446 #if VERBOSE > 4
447         else
448             output("TSS unsupported => stack size unchanged\n");
449 #endif
450 
451         ret = sem_init(&scenarii[i].sem, 0, 0);
452         if (ret == -1)
453             UNRESOLVED(errno, "Unable to init a semaphore");
454 
455     }
456 #if VERBOSE > 0
457     output("All %i thread attribute objects were initialized\n\n", NSCENAR);
458 #endif
459 }
460 
461 /*
462  * This function will free all resources consumed
463  * in the scenar_init() routine
464  */
scenar_fini(void)465 static void scenar_fini(void)
466 {
467     int ret = 0;
468     unsigned int i;
469 
470     for (i = 0; i < NSCENAR; i++) {
471         if (scenarii[i].bottom != NULL)
472             free(scenarii[i].bottom);
473 
474         ret = sem_destroy(&scenarii[i].sem);
475         if (ret == -1)
476             UNRESOLVED(errno, "Unable to destroy a semaphore");
477 
478         ret = pthread_attr_destroy(&scenarii[i].ta);
479         if (ret != 0)
480             UNRESOLVED(ret, "Failed to destroy a thread"
481                    " attribute object");
482     }
483 }
484 
485 static unsigned int sc;
486 
487 #ifdef STD_MAIN
488 
489 static void *threaded(void *arg);
490 
main(void)491 int main(void)
492 {
493     int ret = 0;
494     pthread_t child;
495 
496     output_init();
497     scenar_init();
498 
499     for (sc = 0; sc < NSCENAR; sc++) {
500 #if VERBOSE > 0
501         output("-----\n");
502         output("Starting test with scenario (%i): %s\n",
503                sc, scenarii[sc].descr);
504 #endif
505 
506         ret = pthread_create(&child, &scenarii[sc].ta, threaded, NULL);
507         switch (scenarii[sc].result) {
508         case 0: /* Operation was expected to succeed */
509             if (ret != 0)
510                 UNRESOLVED(ret, "Failed to create this thread");
511             break;
512         case 1: /* Operation was expected to fail */
513             if (ret == 0)
514                 UNRESOLVED(-1, "An error was expected but the"
515                        " thread creation succeeded");
516             break;
517         case 2: /* We did not know the expected result */
518         default:
519 #if VERBOSE > 0
520             if (ret == 0)
521                 output("Thread has been created successfully"
522                        " for this scenario\n");
523             else
524                 output("Thread creation failed with the error:"
525                        " %s\n", strerror(ret));
526 #endif
527         }
528         if (ret == 0) {
529             if (scenarii[sc].detached == 0) {
530                 ret = pthread_join(child, NULL);
531                 if (ret != 0)
532                     UNRESOLVED(ret, "Unable to join a"
533                            " thread");
534             } else {
535                 /* Just wait for the thread to terminate */
536                 do {
537                     ret = sem_wait(&scenarii[sc].sem);
538                 } while ((ret == -1) && (errno == EINTR));
539                 if (ret == -1)
540                     UNRESOLVED(errno, "Failed to wait for"
541                            " the semaphore");
542             }
543         }
544     }
545 
546     scenar_fini();
547 #if VERBOSE > 0
548     output("-----\n");
549     output("All test data destroyed\n");
550     output("Test PASSED\n");
551 #endif
552 
553     PASSED;
554 }
555 #endif
556