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
25struct __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	     */
145CASE_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 */
194void 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 sucessfully\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 sucessfully\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 sucessfully\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 sucessfully 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 */
465void 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
485unsigned int sc;
486
487#ifdef STD_MAIN
488
489extern void *threaded(void *arg);
490
491int 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