| /* |
| * 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") |
| |
| /* We create several instances of the basic cases for the pthread_self testing */ |
| /* 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") |
| /* 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") |
| /* 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 |