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 
28 struct __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 */
scenar_init()123 void 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 */
scenar_fini(void)338 void 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 
355 int sc=0; /* This might be very dirty... but is much simpler */
356 
357 #ifdef STD_MAIN /* We want main to be defined here */
358 
359 extern void * threaded(void *arg); /* This is the test function */
360 
main(int argc,char * argv[])361 int 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