blob: 9886a2fe34f34c879f988f842fbd4276769cae42 [file] [log] [blame]
/*
* Copyright (c) 2004, Bull S.A.. All rights reserved.
* Created by: Sebastien Decugis
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
* This file is a helper file for the pthread_create tests
* It defines the following objects:
* scenarii: array of struct __scenario type.
* NSCENAR : macro giving the total # of scenarii
* scenar_init(): function to call before use the scenarii array.
* scenar_fini(): function to call after end of use of the scenarii array.
*
*/
struct __scenario
{
/* Object to hold the given configuration, and which will be used to create the threads */
pthread_attr_t ta;
/* General parameters */
int detached; /* 0 => joinable; 1 => detached */
/* Scheduling parameters */
int explicitsched; /* 0 => sched policy is inherited; 1 => sched policy from the attr param */
int schedpolicy; /* 0 => default; 1=> SCHED_FIFO; 2=> SCHED_RR */
int schedparam; /* 0 => default sched param; 1 => max value for sched param; -1 => min value for sched param */
int altscope; /* 0 => default contension scope; 1 => alternative contension scope */
/* Stack parameters */
int altstack; /* 0 => system manages the stack; 1 => stack is provided */
int guard; /* 0 => default guardsize; 1=> guardsize is 0; 2=> guard is 1 page -- this setting only affect system stacks (not user's). */
int altsize; /* 0 => default stack size; 1 => stack size specified (min value) -- ignored when stack is provided */
/* Additionnal information */
char * descr; /* object description */
void * bottom; /* Stores the stack start when an alternate stack is required */
int result; /* This thread creation is expected to: 0 => succeed; 1 => fail; 2 => unknown */
sem_t sem; /* This semaphore is used to signal the end of the detached threads execution */
} scenarii[]=
#define CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,res) \
{ \
.detached=det, \
.explicitsched=expl, \
.schedpolicy=scp, \
.schedparam=spa, \
.altscope=sco, \
.altstack=sta, \
.guard=gua, \
.altsize=ssi, \
.descr=desc, \
.bottom=NULL, \
.result=res }
#define CASE_POS(det,expl,scp,spa,sco,sta,gua,ssi,desc) CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,0)
#define CASE_NEG(det,expl,scp,spa,sco,sta,gua,ssi,desc) CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,1)
#define CASE_UNK(det,expl,scp,spa,sco,sta,gua,ssi,desc) CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,2)
/*
* This array gives the different combinations of threads attributes for the testcases.
*
* Some combinations must be avoided.
* -> Do not have a detached thread use an alternative stack;
* as we don't know when the thread terminates to free the stack memory
* -> ... (to be completed)
*
*/
{
/* Unary tests */
/* 0*/ CASE_POS(0, 0, 0, 0, 0, 0, 0, 0, "default")
/* 1*/ ,CASE_POS(1, 0, 0, 0, 0, 0, 0, 0, "detached")
/* 2*/ ,CASE_POS(0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched")
/* 3*/ ,CASE_UNK(0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy")
/* 4*/ ,CASE_UNK(0, 0, 2, 0, 0, 0, 0, 0, "RR Policy")
/* 5*/ ,CASE_UNK(0, 0, 0, 1, 0, 0, 0, 0, "Max sched param")
/* 6*/ ,CASE_UNK(0, 0, 0,-1, 0, 0, 0, 0, "Min sched param")
/* 7*/ ,CASE_POS(0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope")
/* 8*/ ,CASE_POS(0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack")
/* 9*/ ,CASE_POS(0, 0, 0, 0, 0, 0, 1, 0, "No guard size")
/*10*/ ,CASE_UNK(0, 0, 0, 0, 0, 0, 2, 0, "1p guard size")
/*11*/ ,CASE_POS(0, 0, 0, 0, 0, 0, 0, 1, "Min stack size")
/* Stack play */
,CASE_POS(0, 0, 0, 0, 0, 0, 1, 1, "Min stack size, no guard")
,CASE_UNK(0, 0, 0, 0, 0, 0, 2, 1, "Min stack size, 1p guard")
,CASE_POS(1, 0, 0, 0, 0, 1, 0, 0, "Detached, Alternative stack")
,CASE_POS(1, 0, 0, 0, 0, 0, 1, 1, "Detached, Min stack size, no guard")
,CASE_UNK(1, 0, 0, 0, 0, 0, 2, 1, "Detached, Min stack size, 1p guard")
/* Scheduling play -- all results are unknown since it might depend on the user priviledges */
,CASE_UNK(0, 1, 1, 1, 0, 0, 0, 0, "Explicit FIFO max param")
,CASE_UNK(0, 1, 2, 1, 0, 0, 0, 0, "Explicit RR max param")
,CASE_UNK(0, 1, 1,-1, 0, 0, 0, 0, "Explicit FIFO min param")
,CASE_UNK(0, 1, 2,-1, 0, 0, 0, 0, "Explicit RR min param")
,CASE_UNK(0, 1, 1, 1, 1, 0, 0, 0, "Explicit FIFO max param, alt scope")
,CASE_UNK(0, 1, 2, 1, 1, 0, 0, 0, "Explicit RR max param, alt scope")
,CASE_UNK(0, 1, 1,-1, 1, 0, 0, 0, "Explicit FIFO min param, alt scope")
,CASE_UNK(0, 1, 2,-1, 1, 0, 0, 0, "Explicit RR min param, alt scope")
,CASE_UNK(1, 1, 1, 1, 0, 0, 0, 0, "Detached, explicit FIFO max param")
,CASE_UNK(1, 1, 2, 1, 0, 0, 0, 0, "Detached, explicit RR max param")
,CASE_UNK(1, 1, 1,-1, 0, 0, 0, 0, "Detached, explicit FIFO min param")
,CASE_UNK(1, 1, 2,-1, 0, 0, 0, 0, "Detached, explicit RR min param")
,CASE_UNK(1, 1, 1, 1, 1, 0, 0, 0, "Detached, explicit FIFO max param, alt scope")
,CASE_UNK(1, 1, 2, 1, 1, 0, 0, 0, "Detached, explicit RR max param, alt scope")
,CASE_UNK(1, 1, 1,-1, 1, 0, 0, 0, "Detached, explicit FIFO min param, alt scope")
,CASE_UNK(1, 1, 2,-1, 1, 0, 0, 0, "Detached, explicit RR min param, alt scope")
};
#define NSCENAR (sizeof(scenarii) / sizeof(scenarii[0]))
/* This function will initialize every pthread_attr_t object in the scenarii array */
void scenar_init()
{
int ret=0;
int i;
int old;
long pagesize, minstacksize;
long tsa, tss, tps;
pagesize =sysconf(_SC_PAGESIZE);
minstacksize =sysconf(_SC_THREAD_STACK_MIN);
tsa =sysconf(_SC_THREAD_ATTR_STACKADDR);
tss =sysconf(_SC_THREAD_ATTR_STACKSIZE);
tps =sysconf(_SC_THREAD_PRIORITY_SCHEDULING);
#if VERBOSE > 0
output("System abilities:\n");
output(" TSA: %li\n", tsa);
output(" TSS: %li\n", tss);
output(" TPS: %li\n", tps);
output(" pagesize: %li\n", pagesize);
output(" min stack size: %li\n", minstacksize);
#endif
if (minstacksize % pagesize)
{
UNTESTED("The min stack size is not a multiple of the page size");
}
for (i=0; i<NSCENAR; i++)
{
#if VERBOSE > 2
output("Initializing attribute for scenario %i: %s\n", i, scenarii[i].descr);
#endif
ret = pthread_attr_init(&scenarii[i].ta);
if (ret != 0) { UNRESOLVED(ret, "Failed to initialize a thread attribute object"); }
/* Set the attributes according to the scenario */
if (scenarii[i].detached == 1)
{
ret = pthread_attr_setdetachstate(&scenarii[i].ta, PTHREAD_CREATE_DETACHED);
if (ret != 0) { UNRESOLVED(ret, "Unable to set detachstate"); }
}
else
{
ret = pthread_attr_getdetachstate(&scenarii[i].ta, &old);
if (ret != 0) { UNRESOLVED(ret, "Unable to get detachstate from initialized attribute"); }
if (old != PTHREAD_CREATE_JOINABLE) { FAILED("The default attribute is not PTHREAD_CREATE_JOINABLE"); }
}
#if VERBOSE > 4
output("Detach state was set sucessfully\n");
#endif
/* Sched related attributes */
if (tps>0) /* This routine is dependent on the Thread Execution Scheduling option */
{
if (scenarii[i].explicitsched == 1)
ret = pthread_attr_setinheritsched(&scenarii[i].ta, PTHREAD_EXPLICIT_SCHED);
else
ret = pthread_attr_setinheritsched(&scenarii[i].ta, PTHREAD_INHERIT_SCHED);
if (ret != 0) { UNRESOLVED(ret, "Unable to set inheritsched attribute"); }
#if VERBOSE > 4
output("inheritsched state was set sucessfully\n");
#endif
}
#if VERBOSE > 4
else
output("TPS unsupported => inheritsched parameter untouched\n");
#endif
if (tps>0) /* This routine is dependent on the Thread Execution Scheduling option */
{
if (scenarii[i].schedpolicy == 1)
{
ret = pthread_attr_setschedpolicy(&scenarii[i].ta, SCHED_FIFO);
}
if (scenarii[i].schedpolicy == 2)
{
ret = pthread_attr_setschedpolicy(&scenarii[i].ta, SCHED_RR);
}
if (ret != 0) { UNRESOLVED(ret, "Unable to set the sched policy"); }
#if VERBOSE > 4
if (scenarii[i].schedpolicy)
output("Sched policy was set sucessfully\n");
else
output("Sched policy untouched\n");
#endif
}
#if VERBOSE > 4
else
output("TPS unsupported => sched policy parameter untouched\n");
#endif
if (scenarii[i].schedparam != 0)
{
struct sched_param sp;
ret = pthread_attr_getschedpolicy(&scenarii[i].ta, &old);
if (ret != 0) { UNRESOLVED(ret, "Unable to get sched policy from attribute"); }
if (scenarii[i].schedparam == 1)
sp.sched_priority = sched_get_priority_max(old);
if (scenarii[i].schedparam == -1)
sp.sched_priority = sched_get_priority_min(old);
ret = pthread_attr_setschedparam(&scenarii[i].ta, &sp);
if (ret != 0) { UNRESOLVED(ret, "Failed to set the sched param"); }
#if VERBOSE > 4
output("Sched param was set sucessfully to %i\n", sp.sched_priority);
}
else
{
output("Sched param untouched\n");
#endif
}
if (tps>0) /* This routine is dependent on the Thread Execution Scheduling option */
{
ret = pthread_attr_getscope(&scenarii[i].ta, &old);
if (ret != 0) { UNRESOLVED(ret, "Failed to get contension scope from thread attribute"); }
if (scenarii[i].altscope != 0)
{
if (old == PTHREAD_SCOPE_PROCESS)
old = PTHREAD_SCOPE_SYSTEM;
else
old = PTHREAD_SCOPE_PROCESS;
ret = pthread_attr_setscope(&scenarii[i].ta, old);
//if (ret != 0) { UNRESOLVED(ret, "Failed to set contension scope"); }
#if VERBOSE > 0
if (ret != 0) { output("WARNING: The TPS option is claimed to be supported but setscope fails\n"); }
#endif
#if VERBOSE > 4
output("Contension scope set to %s\n", old==PTHREAD_SCOPE_PROCESS?"PTHREAD_SCOPE_PROCESS":"PTHREAD_SCOPE_SYSTEM");
}
else
{
output("Contension scope untouched (%s)\n", old==PTHREAD_SCOPE_PROCESS?"PTHREAD_SCOPE_PROCESS":"PTHREAD_SCOPE_SYSTEM");
#endif
}
}
#if VERBOSE > 4
else
output("TPS unsupported => sched contension scope parameter untouched\n");
#endif
/* Stack related attributes */
if ((tss>0) && (tsa>0)) /* This routine is dependent on the Thread Stack Address Attribute
and Thread Stack Size Attribute options */
{
if (scenarii[i].altstack != 0)
{
/* This is slightly more complicated. We need to alloc a new stack
and free it upon test termination */
/* We will alloc with a simulated guardsize of 1 pagesize */
scenarii[i].bottom = malloc(minstacksize + pagesize);
if (scenarii[i].bottom == NULL) { UNRESOLVED(errno,"Unable to alloc enough memory for alternative stack"); }
ret = pthread_attr_setstack(&scenarii[i].ta, scenarii[i].bottom, minstacksize);
if (ret != 0) { UNRESOLVED(ret, "Failed to specify alternate stack"); }
#if VERBOSE > 1
output("Alternate stack created successfully. Bottom=%p, Size=%i\n", scenarii[i].bottom, minstacksize);
#endif
}
}
#if VERBOSE > 4
else
output("TSA or TSS unsupported => No alternative stack\n");
#endif
#ifndef WITHOUT_XOPEN
if (scenarii[i].guard != 0)
{
if (scenarii[i].guard == 1)
ret = pthread_attr_setguardsize(&scenarii[i].ta, 0);
if (scenarii[i].guard == 2)
ret = pthread_attr_setguardsize(&scenarii[i].ta, pagesize);
if (ret != 0) { UNRESOLVED(ret, "Unable to set guard area size in thread stack"); }
#if VERBOSE > 4
output("Guard size set to %i\n", (scenarii[i].guard==1)?1:pagesize);
#endif
}
#endif
if (tss>0) /* This routine is dependent on the Thread Stack Size Attribute option */
{
if (scenarii[i].altsize != 0)
{
ret = pthread_attr_setstacksize(&scenarii[i].ta, minstacksize);
if (ret != 0) { UNRESOLVED(ret, "Unable to change stack size"); }
#if VERBOSE > 4
output("Stack size set to %i (this is the min)\n", minstacksize);
#endif
}
}
#if VERBOSE > 4
else
output("TSS unsupported => stack size unchanged\n");
#endif
ret = sem_init(&scenarii[i].sem, 0,0);
if (ret == -1) { UNRESOLVED(errno, "Unable to init a semaphore"); }
}
#if VERBOSE > 0
output("All %i thread attribute objects were initialized\n\n", NSCENAR);
#endif
}
/* This function will free all resources consumed in the scenar_init() routine */
void scenar_fini(void)
{
int ret = 0, i;
for (i=0; i<NSCENAR; i++)
{
if (scenarii[i].bottom != NULL)
free(scenarii[i].bottom);
ret = sem_destroy(&scenarii[i].sem);
if (ret == -1) { UNRESOLVED(errno, "Unable to destroy a semaphore"); }
ret = pthread_attr_destroy(&scenarii[i].ta);
if (ret != 0) { UNRESOLVED(ret, "Failed to destroy a thread attribute object"); }
}
}
int sc=0; /* This might be very dirty... but is much simpler */
#ifdef STD_MAIN /* We want main to be defined here */
extern void * threaded(void *arg); /* This is the test function */
int main (int argc, char *argv[])
{
int ret=0;
pthread_t child;
/* Initialize output routine */
output_init();
/* Initialize thread attribute objects */
scenar_init();
for (sc=0; sc < NSCENAR; sc++)
{
#if VERBOSE > 0
output("-----\n");
output("Starting test with scenario (%i): %s\n", sc, scenarii[sc].descr);
#endif
ret = pthread_create(&child, &scenarii[sc].ta, threaded, NULL);
switch (scenarii[sc].result)
{
case 0: /* Operation was expected to succeed */
if (ret != 0) { UNRESOLVED(ret, "Failed to create this thread"); }
break;
case 1: /* Operation was expected to fail */
if (ret == 0) { UNRESOLVED(-1, "An error was expected but the thread creation succeeded"); }
break;
case 2: /* We did not know the expected result */
default:
#if VERBOSE > 0
if (ret == 0)
{ output("Thread has been created successfully for this scenario\n"); }
else
{ output("Thread creation failed with the error: %s\n", strerror(ret)); }
#endif
}
if (ret == 0) /* The new thread is running */
{
if (scenarii[sc].detached == 0)
{
ret = pthread_join(child, NULL);
if (ret != 0) { UNRESOLVED(ret, "Unable to join a thread"); }
}
else
{
/* Just wait for the thread to terminate */
do { ret = sem_wait(&scenarii[sc].sem); }
while ((ret == -1) && (errno == EINTR));
if (ret == -1) { UNRESOLVED(errno, "Failed to wait for the semaphore"); }
}
}
}
scenar_fini();
#if VERBOSE > 0
output("-----\n");
output("All test data destroyed\n");
output("Test PASSED\n");
#endif
PASSED;
}
#endif