/* * 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 sample test aims to check the following assertion: * * pthread_create creates a thread with attributes as specified in the attr parameter. * The steps are: * * -> Create a new thread with known parameters. * -> Check the thread behavior conforms to these parameters. * This checking consists in: * -> If an alternative stack has been specified, check that the new thread stack is within this specified area. * -> If stack size and guard size are known, check that accessing the guard size fails. (new process) * -> If we are able to run threads with high priority and known sched policy, check that a high priority thread executes before a low priority thread. (This will be done in another test has it fails with Linux kernel (2.6.8 at least) * -> The previous test could be extended to cross-process threads to check the scope attribute behavior (postponned for now). * (*) The detachstate attribute is not tested cause this would mean a speculative test. Moreover, it is already tested elsewhere. * The test fails if one of those tests fails. */ /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ #define _POSIX_C_SOURCE 200112L /* Some routines are part of the XSI Extensions */ #ifndef WITHOUT_XOPEN #define _XOPEN_SOURCE 600 #endif /********************************************************************************************/ /****************************** standard includes *****************************************/ /********************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include /********************************************************************************************/ /****************************** Test framework *****************************************/ /********************************************************************************************/ #include "testfrmw.h" #include "testfrmw.c" /* This header is responsible for defining the following macros: * UNRESOLVED(ret, descr); * where descr is a description of the error and ret is an int (error code for example) * FAILED(descr); * where descr is a short text saying why the test has failed. * PASSED(); * No parameter. * * Both three macros shall terminate the calling process. * The testcase shall not terminate in any other maneer. * * The other file defines the functions * void output_init() * void output(char * string, ...) * * Those may be used to output information. */ /********************************************************************************************/ /********************************** Configuration ******************************************/ /********************************************************************************************/ #ifndef VERBOSE #define VERBOSE 1 #endif /********************************************************************************************/ /*********************************** Test cases *****************************************/ /********************************************************************************************/ #define STD_MAIN /* This allows main() to be defined in the included file */ #include "threads_scenarii.c" /* This file will define 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. */ /********************************************************************************************/ /*********************************** Real Test *****************************************/ /********************************************************************************************/ /* The overflow function is used to test the stack overflow */ void * overflow(void * arg) { void * current; void * pad[50]; /* We want to consume the stack quickly */ long stacksize = sysconf(_SC_THREAD_STACK_MIN); /* make sure we touch the current stack memory */ int ret=0; pad[1]=NULL; /* so compiler stops complaining about unused variables */ if (arg == NULL) { /* first call */ current = overflow(¤t); /* Terminate the overflow thread */ /* Post the semaphore to unlock the main thread in case of a detached thread */ do { ret = sem_post(&scenarii[sc].sem); } while ((ret == -1) && (errno == EINTR)); if (ret == -1) { UNRESOLVED(errno, "Failed to post the semaphore"); } return NULL; } /* we cast the pointers into long, which might be a problem on some architectures... */ if ( ((long)arg) < ((long)¤t)) { /* the stack is growing up */ if ( ((long)¤t) - ((long)arg) >= stacksize) { output("Growing up stack started below %p and we are currently up to %p\n", arg, ¤t); return (void *)0; } } else { /* the stack is growing down */ if ( ((long)arg) - ((long)¤t) >= stacksize) { output("Growing down stack started upon %p and we are currently down to %p\n", arg, ¤t); return (void *)0; } } /* We are not yet overflowing, so we loop */ { return overflow(arg); } } void * threaded (void * arg) { int ret = 0; #if VERBOSE > 4 output("Thread %i starting...\n", sc); #endif /* Alternate stack test */ if (scenarii[sc].bottom != NULL) { #ifdef WITHOUT_XOPEN output("Unable to test the alternate stack feature; need an integer pointer cast\n"); #else intptr_t stack_start, stack_end, current_pos; stack_start = (intptr_t) scenarii[sc].bottom; stack_end = stack_start + (intptr_t)sysconf(_SC_THREAD_STACK_MIN); current_pos = (intptr_t)&ret; #if VERBOSE > 2 output("Stack bottom: %p\n", scenarii[sc].bottom); output("Stack end : %p (stack is 0x%lx bytes)\n", (void *)stack_end, stack_end - stack_start); output("Current pos : %p\n", &ret); #endif if ((stack_start > current_pos) || (current_pos > stack_end)) { FAILED("The specified stack was not used.\n"); } #endif // WITHOUT_XOPEN } /* Guard size test */ if ((scenarii[sc].bottom == NULL) /* no alternative stack was specified */ && (scenarii[sc].guard == 2) /* guard area size is 1 memory page */ && (scenarii[sc].altsize == 1))/* We know the stack size */ { pid_t child, ctrl; int status; child=fork(); /* We'll test the feature in another process as this test may segfault */ if (child == -1) { UNRESOLVED(errno, "Failed to fork()"); } if (child != 0) /* father */ { /* Just wait for the child and check its return value */ ctrl = waitpid(child, &status, 0); if (ctrl != child) { UNRESOLVED(errno, "Failed to wait for process termination"); } if (WIFEXITED(status)) /* The process exited */ { if (WEXITSTATUS(status) == 0) { FAILED("Overflow into the guard area did not fail"); } if (WEXITSTATUS(status) == PTS_UNRESOLVED) { UNRESOLVED(-1, "The child process returned unresolved status"); } #if VERBOSE > 4 else { output("The child process returned: %i\n", WEXITSTATUS(status)); } } else { output("The child process did not returned\n"); if (WIFSIGNALED(status)) output("It was killed with signal %i\n", WTERMSIG(status)); else output("neither was it killed. (status = %i)\n", status); #endif } } if (child == 0) /* this is the new process */ { pthread_t th; ret = pthread_create(&th, &scenarii[sc].ta, overflow, NULL); /* Create a new thread with the same attributes */ if (ret != 0) { UNRESOLVED(ret, "Unable to create another thread with the same attributes in the new process"); } if (scenarii[sc].detached == 0) { ret = pthread_join(th, 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"); } } /* Terminate the child process here */ exit(0); } } /* Post the semaphore to unlock the main thread in case of a detached thread */ do { ret = sem_post(&scenarii[sc].sem); } while ((ret == -1) && (errno == EINTR)); if (ret == -1) { UNRESOLVED(errno, "Failed to post the semaphore"); } return arg; }