/* * 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: * If a signal is delivered to a thread waiting for a mutex, * upon return from the signal handler the thread resumes * waiting for the mutex as if it had not been interrupted. * The steps are: * -> Create a child thread * -> Child registers a signal handler * -> Child tries to lock a mutex owned by another thread * -> A signal is sent to the child * -> Check that the signal handler executes and then that the thread still waits for the mutex. * -> Release the mutex and check that the child takes it. * -> Do all of this several times with different mutex attributes * * The test shall be considered to FAIL if it hangs! * a call to alarm() might eventually be added but this is a problem under high * system stress. */ /* * - adam.li@intel.com 2004-05-13 * Add to PTS. Please refer to http://nptl.bullopensource.org/phpBB/ * for general information */ /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ #define _POSIX_C_SOURCE 200112L /* We enable the following line to have mutex attributes defined */ #ifndef WITHOUT_XOPEN #define _XOPEN_SOURCE 600 #endif /********************************************************************************************/ /****************************** standard includes *****************************************/ /********************************************************************************************/ #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 case *****************************************/ /********************************************************************************************/ pthread_mutex_t mtx[5]; sem_t semsig, semstart; int ctrl=0; /********* signal handler **********/ void sighdl(int sig) { if (sem_post(&semsig)) { UNRESOLVED(errno, "Sem_post in signal handler"); } } /********** thread *********/ void * threaded(void * arg) { int ret, i; /* We register the signal handler */ struct sigaction sa; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = sighdl; if ((ret = sigaction (SIGUSR1, &sa, NULL))) { UNRESOLVED(ret, "Unable to register signal handler"); } /* Start the real work */ for (i=0; i<5; i++) /* We'll do this with the 5 kinds of mutex */ { if (sem_post(&semstart)) /* Tell the father we are ready */ { UNRESOLVED(errno, "Sem post in thread"); } if ((ret = pthread_mutex_lock(&mtx[i]))) /* Attempt to lock the mutex */ { UNRESOLVED(ret, "Mutex lock failed in thread"); } ctrl++; /* Notify the main we have passed the lock */ if ((ret = pthread_mutex_unlock(&mtx[i]))) /* We don't need the mutex anymore */ { UNRESOLVED(ret, "Mutex unlock failed in thread"); } } return NULL; } /********* main ********/ int main (int argc, char * argv[]) { int ret, i, j; pthread_t th; pthread_mutexattr_t ma[4], *pma[5]; pma[4]=NULL; output_init(); /* Initialize the mutex attributes */ for (i=0; i<4; i++) { pma[i]=&ma[i]; if ((ret = pthread_mutexattr_init(pma[i]))) { UNRESOLVED(ret, "pthread_mutexattr_init"); } } #ifndef WITHOUT_XOPEN if ((ret = pthread_mutexattr_settype(pma[0], PTHREAD_MUTEX_NORMAL))) { UNRESOLVED(ret, "pthread_mutexattr_settype (normal)"); } if ((ret = pthread_mutexattr_settype(pma[1], PTHREAD_MUTEX_ERRORCHECK))) { UNRESOLVED(ret, "pthread_mutexattr_settype (errorcheck)"); } if ((ret = pthread_mutexattr_settype(pma[2], PTHREAD_MUTEX_RECURSIVE))) { UNRESOLVED(ret, "pthread_mutexattr_settype (recursive)"); } if ((ret = pthread_mutexattr_settype(pma[3], PTHREAD_MUTEX_DEFAULT))) { UNRESOLVED(ret, "pthread_mutexattr_settype (default)"); } #if VERBOSE >1 output("Mutex attributes NORMAL,ERRORCHECK,RECURSIVE,DEFAULT initialized\n"); #endif #else #if VERBOSE > 0 output("Mutex attributes NORMAL,ERRORCHECK,RECURSIVE,DEFAULT unavailable\n"); #endif #endif /* Initialize the 5 mutex */ for (i=0; i<5; i++) { if ((ret = pthread_mutex_init(&mtx[i], pma[i]))) { UNRESOLVED(ret, "pthread_mutex_init failed")} if ((ret = pthread_mutex_lock(&mtx[i]))) { UNRESOLVED(ret, "Initial pthread_mutex_lock failed")} } #if VERBOSE >1 output("Mutex objects are initialized\n"); #endif /* We don't need the mutex attribute objects anymore */ for (i=0; i<4; i++) { if ((ret = pthread_mutexattr_destroy(pma[i]))) { UNRESOLVED(ret, "pthread_mutexattr_destroy"); } } /* Initialize the semaphores */ if (sem_init(&semsig, 0, 1)) { UNRESOLVED(errno, "Sem init (1) failed"); } if (sem_init(&semstart, 0, 0)) { UNRESOLVED(errno, "Sem init (0) failed"); } #if VERBOSE >1 output("Going to create the child thread\n"); #endif /* Start the child */ if ((ret = pthread_create(&th, NULL, threaded, NULL))) { UNRESOLVED(ret, "Unable to create the thread"); } #if VERBOSE >1 output("Child created\n"); #endif /* Monitor the child */ for (i=0; i<5; i++) /* We will do this for the 5 kinds of mutex */ { if (sem_wait(&semstart)) /* Wait for the thread to be ready */ { UNRESOLVED(errno, "Unable to wait for the child"); } #if VERBOSE >1 output("Child is ready for iteration %i\n", i+1); #endif ctrl=0; /* init the ctrl var */ /* Send some signals to the thread */ for (j=0; j<10; j++) { if ((ret = sem_wait(&semsig))) { UNRESOLVED(errno, "Sem_wait failed from the signal handler"); } sched_yield(); /* Let the child do its stuff - might be a nanosleep here*/ if ((ret = pthread_kill (th, SIGUSR1))) { UNRESOLVED(ret, "Pthread_kill failed"); } } #if VERBOSE >1 output("Child was killed 10 times\n"); #endif /* Now check the thread is still waiting for the mutex */ if (ctrl != 0) { FAILED("Killed child passed the pthread_mutex_lock without owning it"); } #if VERBOSE >1 output("Control was OK\n"); #endif /* Unlock the mutex so the thread can proceed to the next one */ if ((ret = pthread_mutex_unlock(&mtx[i]))) { UNRESOLVED(ret, "Mutex unlock in main failed"); } } #if VERBOSE >1 output("The test has passed, we are now going to clean up everything.\n"); #endif /* Clean everything: the test has passed */ if ((ret = pthread_join(th, NULL))) { UNRESOLVED(ret, "Unable to join the child"); } for (i=0; i<5; i++) { if ((ret = pthread_mutex_destroy(&mtx[i]))) { UNRESOLVED(ret, "Unable to finally destroy a mutex"); } } if (sem_destroy(&semstart)) { UNRESOLVED(errno, "Unable to destroy semstart semaphore"); } if (sem_destroy(&semsig)) { UNRESOLVED(errno, "Unable to destroy semsig semaphore"); } PASSED; }