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 sample test aims to check the following assertion:
19 *
20 * Once the thread has been created, subsequent changes to the
21 * thread attribute object don't affect the running thread.
22
23 * The steps are:
24 * -> stack grow
25 *    -> create a thread with minimal stack size
26 *    -> change the stack size to a bigger value
27 *    -> check that the thread stack size did not change.
28 * -> stack decrease
29 *    -> create a thread with a known stack size (> minimum)
30 *    -> change the stack size to the min value
31 *    -> check that the thread stack size did not change.
32 * -> sched policy/param change
33 *    -> create a new thread with a known policy
34 *    -> change the policy in the thread attribute and check the thread policy did not change
35 *    -> change the schedparam in the thread attribute and check the thread priority did not change
36 *    -> change the policy in the running thread and check the thread attribute did not change.
37 *    -> change the priority in the running thread and check the thread attribute did not change.
38
39 * The test fails if one of the checking fails
40
41 */
42
43
44 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
45 #define _POSIX_C_SOURCE 200112L
46
47 /* Some routines are part of the XSI Extensions */
48#ifndef WITHOUT_XOPEN
49 #define _XOPEN_SOURCE	600
50#endif
51/********************************************************************************************/
52/****************************** standard includes *****************************************/
53/********************************************************************************************/
54 #include <pthread.h>
55 #include <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include <sched.h>
62 #include <semaphore.h>
63 #include <errno.h>
64 #include <assert.h>
65 #include <sys/wait.h>
66
67
68/********************************************************************************************/
69/******************************   Test framework   *****************************************/
70/********************************************************************************************/
71 #include "testfrmw.h"
72 #include "testfrmw.c"
73 /* This header is responsible for defining the following macros:
74  * UNRESOLVED(ret, descr);
75  *    where descr is a description of the error and ret is an int (error code for example)
76  * FAILED(descr);
77  *    where descr is a short text saying why the test has failed.
78  * PASSED();
79  *    No parameter.
80  *
81  * Both three macros shall terminate the calling process.
82  * The testcase shall not terminate in any other maneer.
83  *
84  * The other file defines the functions
85  * void output_init()
86  * void output(char * string, ...)
87  *
88  * Those may be used to output information.
89  */
90
91/********************************************************************************************/
92/********************************** Configuration ******************************************/
93/********************************************************************************************/
94#ifndef VERBOSE
95#define VERBOSE 1
96#endif
97
98/********************************************************************************************/
99/***********************************    Test cases  *****************************************/
100/********************************************************************************************/
101
102#include "threads_scenarii.c"
103
104/* This file will define the following objects:
105 * scenarii: array of struct __scenario type.
106 * NSCENAR : macro giving the total # of scenarii
107 * scenar_init(): function to call before use the scenarii array.
108 * scenar_fini(): function to call after end of use of the scenarii array.
109 */
110
111
112/********************************************************************************************/
113/***********************************    Real Test   *****************************************/
114/********************************************************************************************/
115
116sem_t semsync[2]; /* These semaphores will only be used in child process! */
117
118/* The overflow function is used to test the stack overflow */
119void * overflow(void * arg)
120{
121	void * current;
122	void * pad[50]; /* We want to consume the stack quickly */
123	long stacksize = sysconf(_SC_THREAD_STACK_MIN); /* make sure we touch the current stack memory */
124
125	pad[1]=NULL; /* so compiler stops complaining about unused variables */
126	int ret = 0;
127
128	if (arg == NULL)
129	{
130		/* first call */
131
132		/* Synchronize with the parent */
133		do { ret = sem_wait(&semsync[0]); }
134		while ((ret == -1) && (errno == EINTR));
135		if (ret == -1)  {  UNRESOLVED(errno, "Failed to wait for the semaphore");  }
136
137
138		/* Go to recursion */
139		current = overflow(&current);
140
141		/* Terminated */
142		do { ret = sem_post(&semsync[1]); }
143		while ((ret == -1) && (errno == EINTR));
144		if (ret == -1)  {  UNRESOLVED(errno, "Failed to post the semaphore");  }
145
146		/* Terminate the overflow thread */
147		return current;
148	}
149
150	/* we cast the pointers into long, which might be a problem on some architectures... */
151	if ( ((long)arg) < ((long)&current))
152	{
153		/* the stack is growing up */
154		if ( ((long)&current) - ((long)arg) >= stacksize)
155		{
156			output("Growing up stack started below %p and we are currently up to %p\n", arg, &current);
157			return (void *)0;
158		}
159	}
160	else
161	{
162		/* the stack is growing down */
163		if ( ((long)arg) - ((long)&current) >= stacksize)
164		{
165			output("Growing down stack started upon %p and we are currently down to %p\n", arg, &current);
166			return (void *)0;
167		}
168	}
169
170	/* We are not yet overflowing, so we loop */
171	return overflow(arg);
172}
173
174/* The following function will return
175  0 if a thread created with the attribute ta is able to fill the stack up to {minstacksize}.
176  1 if the operation failed.
177  2 if an error prevented the test to complete
178
179  If newsize is not 0, the stack size in ta will be set to this value once the thread is created.
180
181*/
182int test_stack(pthread_attr_t * ta, size_t newsize)
183{
184	pid_t child, ctrl;
185	int status;
186	int ret;
187
188	child=fork(); /* We'll test the feature in another process as this test may segfault */
189
190	if (child == -1)
191	{
192		output("Failed to fork (%s)\n", strerror(errno));
193		return 2;
194	}
195
196	if (child != 0) /* father */
197	{
198		/* Just wait for the child and check its return value */
199		ctrl = waitpid(child, &status, 0);
200		if (ctrl != child)
201		{
202			output("Failed to wait for process termination (%s)\n", strerror(errno));
203			return 2;
204		}
205
206		if (WIFEXITED(status)) /* The process exited */
207		{
208			if (WEXITSTATUS(status) == 0)
209			{  return 0;  } /* We were able to fill the stack */
210			if (WEXITSTATUS(status) == PTS_UNRESOLVED)
211			{
212				output("The child process returned unresolved status\n");
213				return 2;
214			}
215			else
216			{
217				output("The child process returned: %i\n", WEXITSTATUS(status));
218				return 2;
219			}
220		}
221		else
222		{
223			#if VERBOSE > 4
224			output("The child process did not return\n");
225			if (WIFSIGNALED(status))
226				output("It was killed with signal %i\n", WTERMSIG(status));
227			else
228				output("neither was it killed. (status = %i)\n", status);
229			#endif
230		}
231
232		return 1;
233	}
234
235	/* else */
236	/* this is the new process */
237	{
238		pthread_t th;
239		void * rc;
240		int detach;
241
242		/* Semaphore to force the child to wait */
243		ret = sem_init(&semsync[0], 0,0);
244		if (ret == -1) {  UNRESOLVED(errno, "Unable to init a semaphore");  }
245		/* Semaphore to detect thread ending */
246		ret = sem_init(&semsync[1], 0,0);
247		if (ret == -1) {  UNRESOLVED(errno, "Unable to init a semaphore");  }
248
249		ret = pthread_create(&th, ta, overflow, NULL);  /* Create a new thread with the same attributes */
250		if (ret != 0) {  UNRESOLVED(ret, "Unable to create a thread in the new process");  }
251
252		/* If we were asked to perform a change on ta, do it now. */
253		if (newsize)
254		{
255			ret = pthread_attr_setstacksize(ta, newsize);
256			if (ret != 0)  {  UNRESOLVED(ret, "Failed to set the new stack size");  }
257		}
258
259		/* Ok the child can run now */
260		do { ret = sem_post(&semsync[0]); }
261		while ((ret == -1) && (errno == EINTR));
262		if (ret == -1)  {  UNRESOLVED(errno, "Failed to post the semaphore");  }
263
264
265		/* Wait for its termination */
266		do { ret = sem_wait(&semsync[1]); }
267		while ((ret == -1) && (errno == EINTR));
268		if (ret == -1)  {  UNRESOLVED(errno, "Failed to wait for the semaphore");  }
269
270		if (ta != NULL)
271		{
272			ret = pthread_attr_getdetachstate(ta, &detach);
273			if (ret != 0)  {  UNRESOLVED(ret, "Failed to get detach state from the thread attribute");  }
274		}
275		else
276		{
277			detach = PTHREAD_CREATE_JOINABLE; /* default */
278		}
279
280		if (detach == PTHREAD_CREATE_JOINABLE)
281		{
282			ret = pthread_join(th, &rc);
283			if (ret != 0)  {  UNRESOLVED(ret, "Unable to join a thread");  }
284			if (rc != (void *)0)
285			{  UNRESOLVED((int)(long)rc, "The overflow function returned an unexpected value");  }
286		}
287
288
289		/* Terminate the child process here */
290		exit(0);
291	}
292}
293
294
295typedef struct
296{
297	pthread_barrier_t bar;
298	int policy;
299	struct sched_param param;
300} testdata_t;
301
302void * schedtest(void * arg)
303{
304	testdata_t * td = (testdata_t *)arg;
305	int newpol, ret=0;
306	struct sched_param newparam;
307
308	/* Read the current sched policy & param */
309	ret = pthread_getschedparam(pthread_self(), &(td->policy), &(td->param));
310	if (ret != 0)  {  UNRESOLVED(ret, "Failed to read current thread policy / param");  }
311
312	/* sync 1 */
313	ret = pthread_barrier_wait(&(td->bar));
314	if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
315
316	/* sync 2 */
317	ret = pthread_barrier_wait(&(td->bar));
318	if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
319
320	/* Read the current sched policy & param */
321	ret = pthread_getschedparam(pthread_self(), &(td->policy), &(td->param));
322	if (ret != 0)  {  UNRESOLVED(ret, "Failed to read current thread policy / param");  }
323
324	/* sync 3 */
325	ret = pthread_barrier_wait(&(td->bar));
326	if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
327
328	/* sync 4 */
329	ret = pthread_barrier_wait(&(td->bar));
330	if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
331
332	/* Change the current sched policy & param */
333	if (td->policy == SCHED_RR)
334		newpol = SCHED_FIFO;
335	else
336		newpol = SCHED_RR;
337
338	newparam.sched_priority = sched_get_priority_max(newpol);
339
340	if (newparam.sched_priority == td->param.sched_priority)
341		newparam.sched_priority--;
342
343	ret = pthread_setschedparam(pthread_self(), newpol, &newparam);
344	#if VERBOSE > 0
345	if (ret != 0)
346		output("Changing the current thread sched policy failed with error: %s\n", strerror(ret));
347	#endif
348	#if VERBOSE > 2
349	else
350		output("Executing thread scheduling policy changed\n");
351	#endif
352
353	/* sync 5 */
354	ret = pthread_barrier_wait(&(td->bar));
355	if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
356
357	/* Post the sem in case of a detached thread */
358	do { ret = sem_post(&scenarii[sc].sem); }
359	while ((ret == -1) && (errno == EINTR));
360	if (ret == -1)  {  UNRESOLVED(errno, "Failed to post the semaphore");  }
361
362	return NULL;
363}
364
365
366
367int main (int argc, char *argv[])
368{
369	int ret=0;
370	int do_stack_tests;
371	int do_sched_tests;
372
373	/* Initialize output routine */
374	output_init();
375
376	/* Test abilities */
377	do_sched_tests = (sysconf(_SC_THREAD_PRIORITY_SCHEDULING)>0?1:0);
378	do_stack_tests = (test_stack(NULL,0)==0?1:0);
379	#if VERBOSE > 0
380	output("Test starting\n Stack tests %s be executed.\n Sched tests %s be executed.\n",
381		do_stack_tests?"will":"won't",
382		do_sched_tests?"will":"won't");
383	#endif
384
385	/* Initialize thread attribute objects */
386	scenar_init();
387
388	for (sc=0; sc < NSCENAR; sc++)
389	{
390		#if VERBOSE > 0
391		output("-----\n");
392		output("Starting test with scenario (%i): %s\n", sc, scenarii[sc].descr);
393		#endif
394
395		if (do_stack_tests)
396		{
397	   /* stack grow test */
398	   		/* We need the thread attribute to specify a minimal stack */
399			if ((scenarii[sc].altstack == 0) && (scenarii[sc].altsize == 1))
400			{
401				#if VERBOSE > 2
402				output("Processing stack grow test\n");
403				#endif
404
405				ret = test_stack(&scenarii[sc].ta, 2*sysconf(_SC_THREAD_STACK_MIN));
406
407				if (ret == 0)
408				{
409					if (scenarii[sc].guard == 2)
410					{
411						FAILED("Changing the stacksize after the thread was created changed the running thread stack size");
412					}
413					#if VERBOSE > 2
414					else
415						output("We were able to overflow the stack, but the guard area is unknow or null\n");
416					#endif
417
418				}
419
420				if ((ret != 2) && (scenarii[sc].result == 1))
421				{
422					UNRESOLVED(-1, "An error was expected but the thread creation succeeded");
423				}
424
425				#if VERBOSE > 2
426				if ((ret == 1))
427				{
428					output("Stack grow test passed\n");
429				}
430
431				if ((ret == 2) && (scenarii[sc].result == 2))
432				{
433					output("Something went wrong -- we don't care in this case\n");
434				}
435				#endif
436
437				if ((ret == 2) && (scenarii[sc].result == 0))
438				{
439					UNRESOLVED(-1, "An unexpected error occured\n");
440				}
441
442				/* Ok, set back the thread attribute object to a correct value */
443				ret = pthread_attr_setstacksize(&scenarii[sc].ta, sysconf(_SC_THREAD_STACK_MIN));
444				if (ret != 0)  {  UNRESOLVED(ret, "Failed to set stacksize back");  }
445			}
446
447
448	   /* stack decrease test */
449			if ((scenarii[sc].altstack == 0) && (scenarii[sc].altsize == 0))
450			{
451				#if VERBOSE > 2
452				output("Processing stack decrease test\n");
453				#endif
454
455				ret = test_stack(&scenarii[sc].ta, sysconf(_SC_THREAD_STACK_MIN));
456
457				if (ret == 1)  {  FAILED("Decreasing the stack size after thread is created had an influence on the thread");  }
458
459				if ((ret == 0) && (scenarii[sc].result == 1))
460				{
461					UNRESOLVED(-1, "An error was expected but the thread creation succeeded");
462				}
463
464				if ((ret == 2) && (scenarii[sc].result == 0))
465				{
466					UNRESOLVED(-1, "An unexpected error occured\n");
467				}
468
469				#if VERBOSE > 2
470				if (ret == 0)
471					output("Stack decrease test passed.\n");
472				else
473					output("Something failed but we don't care here.\n");
474				#endif
475			}
476
477
478		} /* if do_stack_tests */
479
480		if (do_sched_tests)
481		{
482	  /* Sched policy/param change test */
483	  		if (scenarii[sc].explicitsched != 0) /* We need a specified policy */
484			{
485				pthread_t child;
486				int policy_ori, newpol_max;
487				struct sched_param param_ori, tmp;
488
489				testdata_t td;
490
491				#if VERBOSE > 2
492				output("Processing sched policy/param change test\n");
493				#endif
494
495				/* Backup the scenario object */
496				ret = pthread_attr_getschedpolicy(&(scenarii[sc].ta), &policy_ori);
497				if (ret != 0)  {  UNRESOLVED(ret, "Unable to read sched policy from thread attribute");  }
498				ret = pthread_attr_getschedparam(&(scenarii[sc].ta), &param_ori);
499				if (ret != 0)  {  UNRESOLVED(ret, "Unable to read sched param from thread attribute");  }
500
501				/* Initialize the barrier */
502				ret = pthread_barrier_init(&(td.bar), NULL, 2);
503				if (ret != 0)  {  UNRESOLVED(ret, "Unable to initialize the barrier");  }
504
505				/* Create a new thread with this scenario attribute */
506				ret = pthread_create(&child, &(scenarii[sc].ta), schedtest, &td);
507				if (ret != 0)
508				{
509					if (scenarii[sc].result == 0)
510					{  UNRESOLVED(ret , "Failed to create a thread");  }
511
512					#if VERBOSE > 2
513					if (scenarii[sc].result == 2)
514					{
515						output("The thread creation failed -- we don't care\n");
516					}
517					if (scenarii[sc].result == 1)
518					{
519						output("The thread creation failed as expected\n");
520					}
521					#endif
522				}
523				else /* Thread created */
524				{
525					if (scenarii[sc].result == 1)
526					{  UNRESOLVED(-1, "The thread was created where an error was expected");  }
527
528					#if VERBOSE > 2
529					if (scenarii[sc].result == 2)
530						output("Thread is created\n");
531					#endif
532
533					/* sync 1 */
534					ret = pthread_barrier_wait(&(td.bar));
535					if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
536
537					/* Check the new thread reports the attributes */
538					if (td.policy != policy_ori)
539					{  FAILED("The new thread does not report the scheluling policy that was specified"); }
540
541					if (td.param.sched_priority != param_ori.sched_priority)
542					{  FAILED("The new thread does not report the scheduling priority that was specified at creation");  }
543
544					/* Change the thread attribute object policy & param */
545					if (policy_ori == SCHED_RR)
546					{
547						ret = pthread_attr_setschedpolicy(&(scenarii[sc].ta), SCHED_FIFO);
548						newpol_max = sched_get_priority_max(SCHED_FIFO);
549					}
550					else
551					{
552						ret = pthread_attr_setschedpolicy(&(scenarii[sc].ta), SCHED_RR);
553						newpol_max = sched_get_priority_max(SCHED_RR);
554					}
555					if (ret != 0)  {  UNRESOLVED(ret, "Failed to change the attribute object");  }
556
557					if (newpol_max == param_ori.sched_priority)
558						newpol_max--;
559
560					tmp.sched_priority = newpol_max;
561
562					ret = pthread_attr_setschedparam(&(scenarii[sc].ta), &tmp);
563					if (ret != 0)  {  UNRESOLVED(ret, "Failed to set the attribute sched param");  }
564
565					/* sync 2 */
566					ret = pthread_barrier_wait(&(td.bar));
567					if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
568
569					/* sync 3 */
570					ret = pthread_barrier_wait(&(td.bar));
571					if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
572
573					/* Check if the thread saw the change (should not) */
574					if (td.policy != policy_ori)
575					{  FAILED("The new thread does not report the scheluling policy that was specified"); }
576
577					if (td.param.sched_priority != param_ori.sched_priority)
578					{  FAILED("The new thread does not report the scheduling priority that was specified at creation");  }
579
580					/* Check what we can see for the child thread from here */
581					ret = pthread_getschedparam(child, &(td.policy), &(td.param));
582					if (ret != 0)  {  UNRESOLVED(ret, "Failed to read child thread policy / param");  }
583
584					if (td.policy != policy_ori)
585					{  FAILED("The child thread does not report the scheduling policy that was specified at creation"); }
586
587					if (td.param.sched_priority != param_ori.sched_priority)
588					{  FAILED("The child thread does not report the scheduling priority that was specified at creation");  }
589
590					/* Restore the thread attribute */
591					ret = pthread_attr_setschedpolicy(&(scenarii[sc].ta), policy_ori);
592					if (ret != 0)  {  UNRESOLVED(ret, "Unable to read sched policy from thread attribute");  }
593					ret = pthread_attr_setschedparam(&(scenarii[sc].ta), &param_ori);
594					if (ret != 0)  {  UNRESOLVED(ret, "Unable to read sched param from thread attribute");  }
595
596					/* sync 4*/
597					ret = pthread_barrier_wait(&(td.bar));
598					if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
599
600					/* sync 5*/
601					ret = pthread_barrier_wait(&(td.bar));
602					if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {  UNRESOLVED(ret, "Failed to synchronize on the barrier");  }
603
604					/* check if the thread attribute reports a change (should not) */
605					ret = pthread_attr_getschedpolicy(&(scenarii[sc].ta), &(td.policy));
606					if (ret != 0)  {  UNRESOLVED(ret, "Unable to read sched policy from thread attribute");  }
607					ret = pthread_attr_getschedparam(&(scenarii[sc].ta), &(td.param));
608					if (ret != 0)  {  UNRESOLVED(ret, "Unable to read sched param from thread attribute");  }
609
610					if (td.policy != policy_ori)
611					{  FAILED("The child thread does not report the scheduling policy that was specified at creation"); }
612
613					if (td.param.sched_priority != param_ori.sched_priority)
614					{  FAILED("The child thread does not report the scheduling priority that was specified at creation");  }
615
616					/* Wait for the sem and join eventually the thread */
617					do { ret = sem_wait(&scenarii[sc].sem); }
618					while ((ret == -1) && (errno == EINTR));
619					if (ret == -1)  {  UNRESOLVED(errno, "Failed to wait for the semaphore");  }
620
621					if (scenarii[sc].detached == 0)
622					{
623						ret = pthread_join(child, NULL);
624						if (ret != 0)  {  UNRESOLVED(ret, "Unable to join a thread");  }
625					}
626
627					#if VERBOSE > 2
628					output("Sched policy/param change test passed\n");
629					#endif
630				} /* thread created */
631			}
632
633			/* We could also test if the inheritsched does not influence the new thread */
634
635		} /* if do_sched_tests */
636	}
637
638	scenar_fini();
639	#if VERBOSE > 0
640	output("-----\n");
641	output("All test data destroyed\n");
642	output("Test PASSED\n");
643	#endif
644
645	PASSED;
646}
647
648
649
650