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/* 1*/	,CASE_POS( 1, 0, 0, 0, 0, 0, 0, 0, "detached")
82/* 2*/	,CASE_POS( 0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched")
83/* 3*/	,CASE_UNK( 0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy")
84/* 4*/	,CASE_UNK( 0, 0, 2, 0, 0, 0, 0, 0, "RR Policy")
85/* 5*/	,CASE_UNK( 0, 0, 0, 1, 0, 0, 0, 0, "Max sched param")
86/* 6*/	,CASE_UNK( 0, 0, 0,-1, 0, 0, 0, 0, "Min sched param")
87/* 7*/	,CASE_POS( 0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope")
88/* 8*/	,CASE_POS( 0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack")
89/* 9*/	,CASE_POS( 0, 0, 0, 0, 0, 0, 1, 0, "No guard size")
90/*10*/	,CASE_UNK( 0, 0, 0, 0, 0, 0, 2, 0, "1p guard size")
91/*11*/	,CASE_POS( 0, 0, 0, 0, 0, 0, 0, 1, "Min stack size")
92
93   /* Stack play */
94	,CASE_POS( 0, 0, 0, 0, 0, 0, 1, 1, "Min stack size, no guard")
95	,CASE_UNK( 0, 0, 0, 0, 0, 0, 2, 1, "Min stack size, 1p guard")
96	,CASE_POS( 1, 0, 0, 0, 0, 1, 0, 0, "Detached, Alternative stack")
97	,CASE_POS( 1, 0, 0, 0, 0, 0, 1, 1, "Detached, Min stack size, no guard")
98	,CASE_UNK( 1, 0, 0, 0, 0, 0, 2, 1, "Detached, Min stack size, 1p guard")
99
100   /* Scheduling play -- all results are unknown since it might depend on the user priviledges */
101	,CASE_UNK( 0, 1, 1, 1, 0, 0, 0, 0, "Explicit FIFO max param")
102	,CASE_UNK( 0, 1, 2, 1, 0, 0, 0, 0, "Explicit RR max param")
103	,CASE_UNK( 0, 1, 1,-1, 0, 0, 0, 0, "Explicit FIFO min param")
104	,CASE_UNK( 0, 1, 2,-1, 0, 0, 0, 0, "Explicit RR min param")
105	,CASE_UNK( 0, 1, 1, 1, 1, 0, 0, 0, "Explicit FIFO max param, alt scope")
106	,CASE_UNK( 0, 1, 2, 1, 1, 0, 0, 0, "Explicit RR max param, alt scope")
107	,CASE_UNK( 0, 1, 1,-1, 1, 0, 0, 0, "Explicit FIFO min param, alt scope")
108	,CASE_UNK( 0, 1, 2,-1, 1, 0, 0, 0, "Explicit RR min param, alt scope")
109	,CASE_UNK( 1, 1, 1, 1, 0, 0, 0, 0, "Detached, explicit FIFO max param")
110	,CASE_UNK( 1, 1, 2, 1, 0, 0, 0, 0, "Detached, explicit RR max param")
111	,CASE_UNK( 1, 1, 1,-1, 0, 0, 0, 0, "Detached, explicit FIFO min param")
112	,CASE_UNK( 1, 1, 2,-1, 0, 0, 0, 0, "Detached, explicit RR min param")
113	,CASE_UNK( 1, 1, 1, 1, 1, 0, 0, 0, "Detached, explicit FIFO max param, alt scope")
114	,CASE_UNK( 1, 1, 2, 1, 1, 0, 0, 0, "Detached, explicit RR max param, alt scope")
115	,CASE_UNK( 1, 1, 1,-1, 1, 0, 0, 0, "Detached, explicit FIFO min param, alt scope")
116	,CASE_UNK( 1, 1, 2,-1, 1, 0, 0, 0, "Detached, explicit RR min param, alt scope")
117
118};
119
120#define NSCENAR (sizeof(scenarii) / sizeof(scenarii[0]))
121
122/* This function will initialize every pthread_attr_t object in the scenarii array */
123void scenar_init()
124{
125	int ret=0;
126	int i;
127	int old;
128	long pagesize, minstacksize;
129	long tsa, tss, tps;
130
131	pagesize	=sysconf(_SC_PAGESIZE);
132	minstacksize 	=sysconf(_SC_THREAD_STACK_MIN);
133	tsa		=sysconf(_SC_THREAD_ATTR_STACKADDR);
134	tss		=sysconf(_SC_THREAD_ATTR_STACKSIZE);
135	tps		=sysconf(_SC_THREAD_PRIORITY_SCHEDULING);
136
137	#if VERBOSE > 0
138	output("System abilities:\n");
139	output(" TSA: %li\n", tsa);
140	output(" TSS: %li\n", tss);
141	output(" TPS: %li\n", tps);
142	output(" pagesize: %li\n", pagesize);
143	output(" min stack size: %li\n", minstacksize);
144	#endif
145
146
147	if (minstacksize % pagesize)
148	{
149		UNTESTED("The min stack size is not a multiple of the page size");
150	}
151
152	for (i=0; i<NSCENAR; i++)
153	{
154		#if VERBOSE > 2
155		output("Initializing attribute for scenario %i: %s\n", i, scenarii[i].descr);
156		#endif
157
158		ret = pthread_attr_init(&scenarii[i].ta);
159		if (ret != 0)  {  UNRESOLVED(ret, "Failed to initialize a thread attribute object");  }
160
161		/* Set the attributes according to the scenario */
162		if (scenarii[i].detached == 1)
163		{
164			ret = pthread_attr_setdetachstate(&scenarii[i].ta, PTHREAD_CREATE_DETACHED);
165			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set detachstate");  }
166		}
167		else
168		{
169			ret = pthread_attr_getdetachstate(&scenarii[i].ta, &old);
170			if (ret != 0)  {  UNRESOLVED(ret, "Unable to get detachstate from initialized attribute");  }
171			if (old != PTHREAD_CREATE_JOINABLE)  {  FAILED("The default attribute is not PTHREAD_CREATE_JOINABLE");  }
172		}
173		#if VERBOSE > 4
174		output("Detach state was set sucessfully\n");
175		#endif
176
177		/* Sched related attributes */
178		if (tps>0) /* This routine is dependent on the Thread Execution Scheduling option */
179		{
180			if (scenarii[i].explicitsched == 1)
181				ret = pthread_attr_setinheritsched(&scenarii[i].ta, PTHREAD_EXPLICIT_SCHED);
182			else
183				ret = pthread_attr_setinheritsched(&scenarii[i].ta, PTHREAD_INHERIT_SCHED);
184			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set inheritsched attribute");  }
185			#if VERBOSE > 4
186			output("inheritsched state was set sucessfully\n");
187			#endif
188		}
189		#if VERBOSE > 4
190		else
191			output("TPS unsupported => inheritsched parameter untouched\n");
192		#endif
193
194		if (tps>0) /* This routine is dependent on the Thread Execution Scheduling option */
195		{
196			if (scenarii[i].schedpolicy == 1)
197			{
198				ret = pthread_attr_setschedpolicy(&scenarii[i].ta, SCHED_FIFO);
199			}
200			if (scenarii[i].schedpolicy == 2)
201			{
202				ret = pthread_attr_setschedpolicy(&scenarii[i].ta, SCHED_RR);
203			}
204			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set the sched policy");  }
205			#if VERBOSE > 4
206			if (scenarii[i].schedpolicy)
207				output("Sched policy was set sucessfully\n");
208			else
209				output("Sched policy untouched\n");
210			#endif
211		}
212		#if VERBOSE > 4
213		else
214			output("TPS unsupported => sched policy parameter untouched\n");
215		#endif
216
217		if (scenarii[i].schedparam != 0)
218		{
219			struct sched_param sp;
220
221			ret = pthread_attr_getschedpolicy(&scenarii[i].ta, &old);
222			if (ret != 0)  {  UNRESOLVED(ret, "Unable to get sched policy from attribute"); }
223
224			if (scenarii[i].schedparam == 1)
225				sp.sched_priority = sched_get_priority_max(old);
226			if (scenarii[i].schedparam == -1)
227				sp.sched_priority = sched_get_priority_min(old);
228
229			ret = pthread_attr_setschedparam(&scenarii[i].ta, &sp);
230			if (ret != 0)  {  UNRESOLVED(ret, "Failed to set the sched param");  }
231
232		#if VERBOSE > 4
233			output("Sched param was set sucessfully to %i\n", sp.sched_priority);
234		}
235		else
236		{
237			output("Sched param untouched\n");
238		#endif
239		}
240
241		if (tps>0) /* This routine is dependent on the Thread Execution Scheduling option */
242		{
243			ret = pthread_attr_getscope(&scenarii[i].ta, &old);
244			if (ret != 0)  {  UNRESOLVED(ret, "Failed to get contension scope from thread attribute");  }
245
246			if (scenarii[i].altscope != 0)
247			{
248				if (old == PTHREAD_SCOPE_PROCESS)
249					old = PTHREAD_SCOPE_SYSTEM;
250				else
251					old = PTHREAD_SCOPE_PROCESS;
252
253				ret = pthread_attr_setscope(&scenarii[i].ta, old);
254				//if (ret != 0)  {  UNRESOLVED(ret, "Failed to set contension scope");  }
255				#if VERBOSE > 0
256				if (ret != 0)  {  output("WARNING: The TPS option is claimed to be supported but setscope fails\n");  }
257				#endif
258
259			#if VERBOSE > 4
260				output("Contension scope set to %s\n", old==PTHREAD_SCOPE_PROCESS?"PTHREAD_SCOPE_PROCESS":"PTHREAD_SCOPE_SYSTEM");
261			}
262			else
263			{
264				output("Contension scope untouched (%s)\n", old==PTHREAD_SCOPE_PROCESS?"PTHREAD_SCOPE_PROCESS":"PTHREAD_SCOPE_SYSTEM");
265			#endif
266			}
267		}
268		#if VERBOSE > 4
269		else
270			output("TPS unsupported => sched contension scope parameter untouched\n");
271		#endif
272
273		/* Stack related attributes */
274		if ((tss>0) && (tsa>0)) /* This routine is dependent on the Thread Stack Address Attribute
275			                   and Thread Stack Size Attribute options */
276		{
277			if (scenarii[i].altstack != 0)
278			{
279				/* This is slightly more complicated. We need to alloc a new stack
280				and free it upon test termination */
281				/* We will alloc with a simulated guardsize of 1 pagesize */
282				scenarii[i].bottom = malloc(minstacksize + pagesize);
283				if (scenarii[i].bottom == NULL)  {  UNRESOLVED(errno,"Unable to alloc enough memory for alternative stack"); }
284
285				ret = pthread_attr_setstack(&scenarii[i].ta, scenarii[i].bottom, minstacksize);
286				if (ret != 0)  {  UNRESOLVED(ret, "Failed to specify alternate stack");  }
287
288				#if VERBOSE > 1
289				output("Alternate stack created successfully. Bottom=%p, Size=%i\n", scenarii[i].bottom, minstacksize);
290				#endif
291			}
292		}
293		#if VERBOSE > 4
294		else
295			output("TSA or TSS unsupported => No alternative stack\n");
296		#endif
297
298		#ifndef WITHOUT_XOPEN
299		if (scenarii[i].guard != 0)
300		{
301			if (scenarii[i].guard == 1)
302				ret = pthread_attr_setguardsize(&scenarii[i].ta, 0);
303			if (scenarii[i].guard == 2)
304				ret = pthread_attr_setguardsize(&scenarii[i].ta, pagesize);
305			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set guard area size in thread stack");  }
306			#if VERBOSE > 4
307			output("Guard size set to %i\n", (scenarii[i].guard==1)?1:pagesize);
308			#endif
309		}
310		#endif
311
312		if (tss>0) /* This routine is dependent on the Thread Stack Size Attribute option */
313		{
314			if (scenarii[i].altsize != 0)
315			{
316				ret = pthread_attr_setstacksize(&scenarii[i].ta, minstacksize);
317				if (ret != 0)  {  UNRESOLVED(ret, "Unable to change stack size");  }
318				#if VERBOSE > 4
319				output("Stack size set to %i (this is the min)\n", minstacksize);
320				#endif
321			}
322		}
323		#if VERBOSE > 4
324		else
325			output("TSS unsupported => stack size unchanged\n");
326		#endif
327
328		ret = sem_init(&scenarii[i].sem, 0,0);
329		if (ret == -1) {  UNRESOLVED(errno, "Unable to init a semaphore");  }
330
331	}
332	#if VERBOSE > 0
333	output("All %i thread attribute objects were initialized\n\n", NSCENAR);
334	#endif
335}
336
337/* This function will free all resources consumed in the scenar_init() routine */
338void scenar_fini(void)
339{
340	int ret = 0, i;
341
342	for (i=0; i<NSCENAR; i++)
343	{
344		if (scenarii[i].bottom != NULL)
345			free(scenarii[i].bottom);
346
347		ret = sem_destroy(&scenarii[i].sem);
348		if (ret == -1) {  UNRESOLVED(errno, "Unable to destroy a semaphore");  }
349
350		ret = pthread_attr_destroy(&scenarii[i].ta);
351		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy a thread attribute object");  }
352	}
353}
354
355int sc=0; /* This might be very dirty... but is much simpler */
356
357#ifdef STD_MAIN /* We want main to be defined here */
358
359extern void * threaded(void *arg); /* This is the test function */
360
361int main (int argc, char *argv[])
362{
363	int ret=0;
364	pthread_t child;
365
366	/* Initialize output routine */
367	output_init();
368
369	/* Initialize thread attribute objects */
370	scenar_init();
371
372	for (sc=0; sc < NSCENAR; sc++)
373	{
374		#if VERBOSE > 0
375		output("-----\n");
376		output("Starting test with scenario (%i): %s\n", sc, scenarii[sc].descr);
377		#endif
378
379		ret = pthread_create(&child, &scenarii[sc].ta, threaded, NULL);
380		switch (scenarii[sc].result)
381		{
382			case 0: /* Operation was expected to succeed */
383				if (ret != 0)  {  UNRESOLVED(ret, "Failed to create this thread");  }
384				break;
385
386			case 1: /* Operation was expected to fail */
387				if (ret == 0)  {  UNRESOLVED(-1, "An error was expected but the thread creation succeeded");  }
388				break;
389
390			case 2: /* We did not know the expected result */
391			default:
392				#if VERBOSE > 0
393				if (ret == 0)
394					{ output("Thread has been created successfully for this scenario\n"); }
395				else
396					{ output("Thread creation failed with the error: %s\n", strerror(ret)); }
397				#endif
398		}
399		if (ret == 0) /* The new thread is running */
400		{
401			if (scenarii[sc].detached == 0)
402			{
403				ret = pthread_join(child, NULL);
404				if (ret != 0)  {  UNRESOLVED(ret, "Unable to join a thread");  }
405			}
406			else
407			{
408				/* Just wait for the thread to terminate */
409				do { ret = sem_wait(&scenarii[sc].sem); }
410				while ((ret == -1) && (errno == EINTR));
411				if (ret == -1)  {  UNRESOLVED(errno, "Failed to wait for the semaphore");  }
412			}
413		}
414	}
415
416	scenar_fini();
417	#if VERBOSE > 0
418	output("-----\n");
419	output("All test data destroyed\n");
420	output("Test PASSED\n");
421	#endif
422
423	PASSED;
424}
425#endif
426