1/* 2 * Copyright (c) 2004, Bull S.A.. All rights reserved. 3 * Created by: Sebastien Decugis 4 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 * You should have received a copy of the GNU General Public License along 14 * with this program; if not, write the Free Software Foundation, Inc., 59 15 * Temple Place - Suite 330, Boston MA 02111-1307, USA. 16 17 18 * This sample test aims to check the following assertion: 19 * 20 * While a thread is blocked on a conditionnal variable, 21 * a dynamic binding exists between this conditionnal variable 22 * and the mutex which was the second argument. 23 * This dynamic binding stops existing when the last thread is unblocked. 24 * Even if the conditionnal variable can then be reused with another mutex, 25 * the threads which have been unblocked must still acquire 26 * the mutex they had associated with the conditionnal variable at call time. 27 * 28 29 * The steps are: 30 * -> Create two mutexes m1 and m2 (errorcheck or recursive) 31 * -> Create a condition variable c depending on a bootlean b 32 * -> create N threads which will 33 * -> lock m1 34 * -> wait or timedwait c, m1 (while b is false) 35 * -> check it owns m1 (check depends on the mutex type) 36 * -> lock m2 37 * -> wait or timedwait c, m2 (while b is false) 38 * -> check it owns m2 39 * -> mark this thread as terminate 40 * -> Once all threads are waiting on (c,m1), 41 * mark b as true then broadcast c until all threads are terminated. 42 */ 43 44 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ 45 #define _POSIX_C_SOURCE 200112L 46 47 /* We need the XSI extention for the mutex attributes */ 48#ifndef WITHOUT_XOPEN 49 #define _XOPEN_SOURCE 600 50#endif 51 /********************************************************************************************/ 52/****************************** standard includes *****************************************/ 53/********************************************************************************************/ 54 #include <pthread.h> 55 #include <stdarg.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <unistd.h> 59 60 #include <time.h> 61 62/********************************************************************************************/ 63/****************************** Test framework *****************************************/ 64/********************************************************************************************/ 65 #include "testfrmw.h" 66 #include "testfrmw.c" 67 /* This header is responsible for defining the following macros: 68 * UNRESOLVED(ret, descr); 69 * where descr is a description of the error and ret is an int (error code for example) 70 * FAILED(descr); 71 * where descr is a short text saying why the test has failed. 72 * PASSED(); 73 * No parameter. 74 * 75 * Both three macros shall terminate the calling process. 76 * The testcase shall not terminate in any other maneer. 77 * 78 * The other file defines the functions 79 * void output_init() 80 * void output(char * string, ...) 81 * 82 * Those may be used to output information. 83 */ 84 85/********************************************************************************************/ 86/********************************** Configuration ******************************************/ 87/********************************************************************************************/ 88#ifndef VERBOSE 89#define VERBOSE 1 90#endif 91 92#define NTHREADS (100) 93 94#ifndef WITHOUT_ALTCLK 95#define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ 96#endif 97 98/********************************************************************************************/ 99/*********************************** Test case *****************************************/ 100/********************************************************************************************/ 101#ifndef WITHOUT_XOPEN 102 103struct _td 104{ 105 pthread_mutex_t mtx1, mtx2; /* The two mutex m1 and m2 */ 106 pthread_cond_t cnd; /* The cond var c */ 107 char boolcnd; /* The boolean predicate b associated with c */ 108 int type; /* Type of mutex */ 109 clockid_t cid; /* Clock used by cond c */ 110 int started; /* # of threads which are already waiting */ 111 int stopped; /* # of threads which are terminated */ 112} data; 113 114void * threaded ( void * arg ) 115{ 116 int ret; 117 118 struct timespec ts; 119 120 /* Prepare the timeout parameter */ 121 ret = clock_gettime(data.cid, &ts); 122 if (ret != 0) { UNRESOLVED(ret, "Unable to get time from clock"); } 123 ts.tv_sec += 30; 124 125 /* Lock m1 */ 126 ret = pthread_mutex_lock(&(data.mtx1)); 127 if (ret != 0) { UNRESOLVED(ret, "Unable tu lock m1 in thread"); } 128 129 /* Tell the parent this thread is started */ 130 data.started++; 131 132 /* wait for the cond - bind the cond to the mutex m1 */ 133 do 134 { 135 if (arg == (void *)0) 136 ret = pthread_cond_wait(&(data.cnd), &(data.mtx1)); 137 else 138 ret = pthread_cond_timedwait(&(data.cnd), &(data.mtx1), &ts); 139 } while ((ret == 0) && (data.boolcnd == 0)); 140 if (ret != 0) { UNRESOLVED(ret, "First wait failed in thread"); } 141 142 /* Test ownership and unlock m1 */ 143 if (data.type == PTHREAD_MUTEX_RECURSIVE) 144 { 145 ret = pthread_mutex_trylock(&(data.mtx1)); 146 if (ret != 0) { FAILED("Unable to re-lock recursive mutex after cond wait"); } 147 ret = pthread_mutex_unlock(&(data.mtx1)); 148 if (ret != 0) { UNRESOLVED(ret, "Mutex unlock failed"); } 149 } 150 ret = pthread_mutex_unlock(&(data.mtx1)); 151 if (ret != 0) { FAILED("Unable to unlock m1 in thread - not owner?"); } 152 ret = pthread_mutex_unlock(&(data.mtx1)); 153 if (ret == 0) { FAILED("Unlocking an unlocked mutex succeeded"); } /* Failed while this is not a default mutex */ 154 155 /* Lock m2 */ 156 ret = pthread_mutex_lock(&(data.mtx2)); 157 if (ret != 0) { UNRESOLVED(ret, "Unable tu lock m2 in thread"); } 158 159 /* wait for the cond - bind the cond to the mutex m2 */ 160 do 161 { 162 if (arg == (void *)0) 163 ret = pthread_cond_wait(&(data.cnd), &(data.mtx2)); 164 else 165 ret = pthread_cond_timedwait(&(data.cnd), &(data.mtx2), &ts); 166 } while ((ret == 0) && (data.boolcnd == 0)); 167 if (ret != 0) { UNRESOLVED(ret, "Second wait failed in thread"); } 168 169 /* Mark the thread as terminated while we are protected by m2 */ 170 data.stopped++; 171 172 /* Test ownership and unlock m2*/ 173 if (data.type == PTHREAD_MUTEX_RECURSIVE) 174 { 175 ret = pthread_mutex_trylock(&(data.mtx2)); 176 if (ret != 0) { FAILED("Unable to re-lock recursive mutex after cond wait"); } 177 ret = pthread_mutex_unlock(&(data.mtx2)); 178 if (ret != 0) { UNRESOLVED(ret, "Mutex unlock failed"); } 179 } 180 ret = pthread_mutex_unlock(&(data.mtx2)); 181 if (ret != 0) { FAILED("Unable to unlock m2 in thread - not owner?"); } 182 ret = pthread_mutex_unlock(&(data.mtx2)); 183 if (ret == 0) { FAILED("Unlocking an unlocked mutex succeeded"); } /* Failed while this is not a default mutex */ 184 185 return NULL; 186} 187 188 189int main(int argc, char * argv[]) 190{ 191 int ret, i, j; 192 pthread_mutexattr_t ma; 193 pthread_condattr_t ca; 194 pthread_t th[NTHREADS]; 195 int loc_started, loc_stopped; 196 197 long altclk_ok, pshared_ok; 198 199 struct 200 { 201 char altclk; /* Want to use alternative clock */ 202 char pshared; /* Want to use process-shared primitives */ 203 int type; /* mutex type */ 204 char * descr; /* Description of the case */ 205 206 } scenar[] = 207 { {0, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex" } 208 ,{0, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex" } 209 #ifdef USE_ALTCLK 210 ,{1, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex + altclock cond" } 211 ,{1, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex + altclock cond" } 212 ,{1, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex + altclock cond" } 213 ,{1, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex + altclock cond" } 214 #endif 215 ,{0, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex" } 216 ,{0, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex" } 217 }; 218 219 output_init(); 220 221 /* Initialize the constants */ 222 altclk_ok = sysconf(_SC_CLOCK_SELECTION); 223 if (altclk_ok > 0) 224 altclk_ok = sysconf(_SC_MONOTONIC_CLOCK); 225 226#ifndef USE_ALTCLK 227 if (altclk_ok > 0) 228 output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n"); 229#endif 230 231 232 pshared_ok = sysconf(_SC_THREAD_PROCESS_SHARED); 233 234 #if VERBOSE > 0 235 output("Test starting\n"); 236 output(" Process-shared primitive %s be tested\n", (pshared_ok>0)?"will":"won't"); 237 output(" Alternative clock for cond %s be tested\n", (altclk_ok>0)?"will":"won't"); 238 #endif 239 240 for (i=0; i< (sizeof(scenar) / sizeof(scenar[0])); i++) 241 { 242 #if VERBOSE > 1 243 output("Starting test for %s\n", scenar[i].descr); 244 #endif 245 246 /* Initialize the data structure */ 247 ret = pthread_mutexattr_init(&ma); 248 if (ret != 0) { UNRESOLVED(ret, "Mutex attribute object init failed"); } 249 250 ret = pthread_mutexattr_settype(&ma, scenar[i].type); 251 if (ret != 0) { UNRESOLVED(ret, "Unable to set mutex type"); } 252 253 if ((pshared_ok > 0) && (scenar[i].pshared != 0)) 254 { 255 ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); 256 if (ret != 0) { UNRESOLVED(ret, "Unable to set mutex process-shared"); } 257 } 258 259 ret = pthread_condattr_init(&ca); 260 if (ret != 0) { UNRESOLVED(ret, "Cond attribute object init failed"); } 261 262 if ((pshared_ok > 0) && (scenar[i].pshared != 0)) 263 { 264 ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED); 265 if (ret != 0) { UNRESOLVED(ret, "Unable to set cond process-shared"); } 266 } 267 268 #ifdef USE_ALTCLK 269 if ((altclk_ok > 0) && (scenar[i].altclk != 0)) 270 { 271 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); 272 if (ret != 0) { UNRESOLVED(ret, "Unable to set alternative (monotonic) clock for cond"); } 273 } 274 #endif 275 276 ret = pthread_mutex_init(&(data.mtx1), &ma); 277 if (ret != 0) { UNRESOLVED(ret, "Unable to init mutex 1"); } 278 279 ret = pthread_mutex_init(&(data.mtx2), &ma); 280 if (ret != 0) { UNRESOLVED(ret, "Unable to init mutex 2"); } 281 282 ret = pthread_cond_init(&(data.cnd), &ca); 283 if (ret != 0) { UNRESOLVED(ret, "Unable to initialize condvar"); } 284 285 data.boolcnd = 0; 286 287 ret = pthread_mutexattr_gettype(&ma, &(data.type)); 288 if (ret != 0) { UNRESOLVED(ret, "Unable to get type from mutex attr"); } 289 290 #ifdef USE_ALTCLK 291 ret = pthread_condattr_getclock(&ca, &(data.cid)); 292 if (ret != 0) { UNRESOLVED(ret, "Unable to get clock ID from cond attr"); } 293 #else 294 data.cid = CLOCK_REALTIME; 295 #endif 296 297 data.started = 0; 298 data.stopped = 0; 299 300 /* Start the threads */ 301 #if VERBOSE > 1 302 output("Initialization OK, starting threads\n"); 303 #endif 304 for (j = 0; j < NTHREADS; j++) 305 { 306 ret = pthread_create(&th[j], NULL, threaded, (void *)(long)(j & 1)); 307 if (ret != 0) { UNRESOLVED(ret, "Thread creation failed"); } 308 } 309 310 /* Wait for the threads to be started */ 311 do { 312 ret = pthread_mutex_lock(&(data.mtx1)); 313 if (ret != 0) { UNRESOLVED(ret, "Unable to lock m1 in parent"); } 314 loc_started = data.started; 315 ret = pthread_mutex_unlock(&(data.mtx1)); 316 if (ret != 0) { UNRESOLVED(ret, "Unable to unlock m1 in parent"); } 317 } while (loc_started < NTHREADS); 318 319 /* Broadcast the condition until all threads are terminated */ 320 data.boolcnd = 1; 321 do { 322 ret = pthread_cond_broadcast(&(data.cnd)); 323 if (ret != 0) { UNRESOLVED(ret, "Unable to broadcast cnd"); } 324 sched_yield(); 325 ret = pthread_mutex_lock(&(data.mtx2)); 326 if (ret != 0) { UNRESOLVED(ret, "Unable to lock m2 in parent"); } 327 loc_stopped = data.stopped; 328 ret = pthread_mutex_unlock(&(data.mtx2)); 329 if (ret != 0) { UNRESOLVED(ret, "Unable to unlock m2 in parent"); } 330 } while (loc_stopped < NTHREADS); 331 332 /* Join the threads */ 333 for (j = 0; j < NTHREADS; j++) 334 { 335 ret = pthread_join(th[j], NULL); 336 if (ret != 0) { UNRESOLVED(ret, "Thread join failed"); } 337 } 338 339 #if VERBOSE > 1 340 output("Test passed for %s\n", scenar[i].descr); 341 #endif 342 343 /* Destroy data */ 344 ret = pthread_cond_destroy(&(data.cnd)); 345 if (ret != 0) { UNRESOLVED(ret, "Cond destroy failed"); } 346 347 ret = pthread_mutex_destroy(&(data.mtx1)); 348 if (ret != 0) { UNRESOLVED(ret, "Mutex 1 destroy failed"); } 349 350 ret = pthread_mutex_destroy(&(data.mtx2)); 351 if (ret != 0) { UNRESOLVED(ret, "Mutex 2 destroy failed"); } 352 353 ret = pthread_condattr_destroy(&ca); 354 if (ret != 0) { UNRESOLVED(ret, "Cond attribute destroy failed"); } 355 356 ret = pthread_mutexattr_destroy(&ma); 357 if (ret != 0) { UNRESOLVED(ret, "Mutex attr destroy failed"); } 358 } /* Proceed to next case */ 359 360 361 PASSED; 362} 363 364#else /* WITHOUT_XOPEN */ 365int main(int argc, char * argv[]) 366{ 367 output_init(); 368 UNTESTED("This test requires XSI features"); 369} 370#endif 371 372