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