/* * 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: * * The macro PTHREAD_MUTEX_INITIALIZER can be used * to initialize mutexes that are statically allocated. * The effect are equivalent to dynamic initialization by a call to * pthread_mutex_init() with parameter attr specified as NULL, * except that no error checks are performed. * * The steps are: * * create two mutexes. One is initialized with NULL attribute, * the other is statically initialized with the macro PTHREAD_MUTEX_INITIALIZER. * * Compare the following features between the two mutexes: * -> Can it cause / detect a deadlock? (attempt to lock a mutex the thread already owns). * If detected, do both mutexes cause the same error code? * -> Is an error returned when unlocking the mutex in unlocked state? * When unlocking the mutex owned by another thread? * * * The test will pass if the results of each feature are the same for the two mutexes * (making no assumption on what is the default behavior). * The test will be unresolved if any initialization fails. * The test will fail if a feature differs between the two mutex objects. */ /* * - adam.li@intel.com 2004-05-09 * 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 /********************************************************************************************/ /****************************** standard includes *****************************************/ /********************************************************************************************/ #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 *****************************************/ /********************************************************************************************/ /**** global variables ****/ pthread_mutex_t * p_mtx; int retval = 0; int returned = 0; int canceled = 0; sem_t semA, semB; /***** Cancelation handlers *****/ void cleanup_deadlk(void * arg) { canceled = 1; pthread_mutex_unlock(p_mtx); } /***** Threads functions *****/ void * deadlk_issue(void * arg) { int ret, tmp; if ((ret=pthread_mutex_lock(p_mtx))) { UNRESOLVED(ret, "First mutex lock in deadlk_issue"); } pthread_cleanup_push(cleanup_deadlk, NULL); if ((ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &tmp))) { UNRESOLVED(ret, "Set cancel type in deadlk_issue"); } if ((ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &tmp))) { UNRESOLVED(ret, "Set cancel state in deadlk_issue"); } #if VERBOSE >1 output("Thread releases the semaphore...\n"); #endif if ((ret = sem_post(&semA))) { UNRESOLVED(errno, "Sem_post in deadlk_issue"); } returned = 0; retval = pthread_mutex_lock(p_mtx); returned = 1; if ((ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &tmp))) { UNRESOLVED(ret, "Set cancel state in deadlk_issue"); } pthread_cleanup_pop(0); return NULL; } void * unlock_issue(void * arg) { int ret; #if VERBOSE >1 output("Locking in child...\n"); #endif if ((ret=pthread_mutex_lock(p_mtx))) { UNRESOLVED(ret, "First mutex lock in unlock_issue"); } if ((ret = sem_post(&semA))) { UNRESOLVED(errno, "Sem_post in unlock_issue"); } if ((ret = sem_wait(&semB))) { UNRESOLVED(errno, "Sem_wait in unlock_issue"); } if (retval != 0) /* parent thread failed to unlock the mutex) */ { #if VERBOSE >1 output("Unlocking in child...\n"); #endif if ((ret=pthread_mutex_unlock(p_mtx))) { FAILED("Mutex unlock returned an error but mutex is unlocked."); } } return NULL; } /***** main program *****/ int main(int argc, char *argv[]) { pthread_mutex_t mtx_null, mtx_macro = PTHREAD_MUTEX_INITIALIZER; pthread_t thr; pthread_mutex_t * tab_mutex[2]={&mtx_null, &mtx_macro}; int tab_res[2][3]={{0,0,0},{0,0,0}}; int ret; void * th_ret; int i; output_init(); #if VERBOSE >1 output("Test starting...\n"); #endif /* We first initialize the two mutexes. */ if ((ret=pthread_mutex_init(&mtx_null, NULL))) { UNRESOLVED(ret, "NULL mutex init"); } if ((ret=sem_init(&semA, 0, 0))) { UNRESOLVED(errno, "Sem A init"); } if ((ret=sem_init(&semB, 0, 0))) { UNRESOLVED(errno, "Sem B init"); } #if VERBOSE >1 output("Data initialized...\n"); #endif /* OK let's go for the first part of the test : abnormals unlocking */ /* We first check if unlocking an unlocked mutex returns an error. */ retval = pthread_mutex_unlock(tab_mutex[0]); ret = pthread_mutex_unlock(tab_mutex[1]); #if VERBOSE >0 output("Results for unlock issue #1:\n mutex 1 unlocking returned %i\n mutex 2 unlocking returned %i\n", retval, ret); #endif if (ret != retval) { FAILED("Unlocking an unlocked mutex behaves differently."); } /* Now we focus on unlocking a mutex lock by another thread */ for (i=0; i<2; i++) { p_mtx = tab_mutex[i]; tab_res[i][0]=0; tab_res[i][1]=0; tab_res[i][2]=0; #if VERBOSE >1 output("Creating thread (unlock)...\n"); #endif if ((ret = pthread_create(&thr, NULL, unlock_issue, NULL))) { UNRESOLVED(ret, "Unlock issue thread create"); } if ((ret = sem_wait(&semA))) { UNRESOLVED(errno, "Sem A wait failed for unlock issue."); } #if VERBOSE >1 output("Unlocking in parent...\n"); #endif retval = pthread_mutex_unlock(p_mtx); if ((ret = sem_post(&semB))) { UNRESOLVED(errno, "Sem B post failed for unlock issue."); } if ((ret=pthread_join(thr, &th_ret))) { UNRESOLVED(ret, "Join thread"); } #if VERBOSE >1 output("Thread joined successfully...\n"); #endif tab_res[i][0] = retval; } #if VERBOSE >0 output("Results for unlock issue #2:\n mutex 1 returned %i\n mutex 2 returned %i\n", tab_res[0][0],tab_res[1][0]); #endif if (tab_res[0][0] != tab_res[1][0]) { FAILED("Unlocking an unowned mutex behaves differently."); } /* We now are going to test the deadlock issue */ /* We start with testing the NULL mutex features */ for (i=0; i<2; i++) { p_mtx = tab_mutex[i]; tab_res[i][0]=0; tab_res[i][1]=0; tab_res[i][2]=0; #if VERBOSE >1 output("Creating thread (deadlk)...\n"); #endif if ((ret = pthread_create(&thr, NULL, deadlk_issue, NULL))) { UNRESOLVED(ret, "Deadlk_issue thread create"); } /* Now we are waiting the thread is ready to relock the mutex. */ if ((ret=sem_wait(&semA))) { UNRESOLVED(errno, "Sem wait"); } /* To ensure thread runs until second lock, we yield here */ sched_yield(); /* OK, now we cancel the thread */ canceled=0; #if VERBOSE >1 output("Cancel thread...\n"); #endif if (returned ==0) if ((ret=pthread_cancel(thr))) { UNRESOLVED(ret, "Cancel thread (deadlk_issue)"); } #if VERBOSE >1 output("Thread canceled...\n"); #endif if ((ret=pthread_join(thr, &th_ret))) { UNRESOLVED(ret, "Join thread"); } #if VERBOSE >1 output("Thread joined successfully...\n"); #endif tab_res[i][2] = retval; tab_res[i][1] = returned; tab_res[i][0] = canceled; } /* Now we parse the results */ #if VERBOSE >0 output("Results for deadlock issue:\n mutex 1 \t%s\t%s%i\n mutex 2 \t%s\t%s%i\n", tab_res[0][0]?"deadlock" : "no deadlock", tab_res[0][1]?"returned " : "did not return ", tab_res[0][2], tab_res[1][0]?"deadlock" : "no deadlock", tab_res[1][1]?"returned " : "did not return ", tab_res[1][2]); #endif if (tab_res[0][0] != tab_res[1][0]) { FAILED("One mutex deadlocks, not the other"); } if (tab_res[0][1] != tab_res[1][1]) { UNRESOLVED(tab_res[0][1], "Abnormal situation!"); } if ((tab_res[0][1] == 1) && (tab_res[0][2] != tab_res[1][2])) { FAILED("The locks returned different error codes."); } PASSED; }