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