1 /* 2 * Copyright (c) International Business Machines Corp., 2004. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of version 2 of the GNU General Public License as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it would be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 * 12 * You should have received a copy of the GNU General Public License along 13 * with this program; if not, write the Free Software Foundation, Inc., 14 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 15 * 16 */ 17 /********************************************************** 18 * 19 * TEST IDENTIFIER : nptl01 20 * 21 * EXECUTED BY : root 22 * 23 * TEST TITLE : NPTL test for pthread_cond_timedwait() error 24 * path bug. 25 * 26 * TEST CASE TOTAL : 1 27 * 28 * AUTHOR : Neil Richards <neil_richards (at) uk.ibm.com> 29 * 30 * DESCRIPTION 31 * This is a test for a bug found in the pthread_cond_timedwait() system call. 32 * of the Native POSIX Thread Library (NPTL) library code. 33 * There was an error path in the system call, where the sequence counters were 34 * getting updated w/o holding the internal condvar lock. A FAIL is indicated 35 * by the test hanging and not completing execution. 36 * 37 ****************************************************************/ 38 #define _GNU_SOURCE 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <pthread.h> 43 #include <errno.h> 44 #include <unistd.h> 45 #include <time.h> 46 #include <sys/time.h> 47 #include "test.h" 48 49 #define MAXTIME 72000 /* Maximum # of secs to wait before failing */ 50 #define NUMLOOPS 100000 /* # of loops */ 51 52 char *TCID = "nptl01"; /* Test program identifier. */ 53 int TST_TOTAL = 1; /* Total number of test cases. */ 54 void cleanup(); 55 56 pthread_mutex_t req; 57 pthread_mutex_t ack; 58 pthread_mutex_t wait; 59 pthread_cond_t parent; 60 pthread_cond_t child; 61 int idle_count = 0; 62 63 /* 64 * The time to wait should be set appropriately so that the waiting thread 65 * is coming out of the wait at around the same time as the other thread is 66 * signalling it. 67 * The value of 1000 seems to work (ie. demonstrate the problem) on my 68 * 8 way (hyperthreaded) 2GHz Xeon box. 69 */ 70 #define NSECS_TO_WAIT (1) 71 72 void call_mutex_init(pthread_mutex_t * mutex, char *buf, size_t buf_len) 73 { 74 int ret; 75 76 if ((ret = pthread_mutex_init(mutex, NULL)) != 0) { 77 tst_brkm(TBROK, cleanup, "pthread_mutex_init failed: %s", 78 strerror_r(ret, buf, buf_len)); 79 } 80 } 81 82 void call_mutex_lock(pthread_mutex_t * mutex, char *buf, size_t buf_len) 83 { 84 int ret; 85 86 if ((ret = pthread_mutex_lock(mutex)) != 0) { 87 tst_brkm(TBROK, cleanup, "pthread_mutex_lock failed: %s", 88 strerror_r(ret, buf, buf_len)); 89 } 90 } 91 92 void call_mutex_unlock(pthread_mutex_t * mutex, char *buf, size_t buf_len) 93 { 94 int ret; 95 96 if ((ret = pthread_mutex_unlock(mutex)) != 0) { 97 tst_brkm(TBROK, cleanup, "pthread_mutex_unlock failed: %s", 98 strerror_r(ret, buf, buf_len)); 99 } 100 } 101 102 void call_cond_init(pthread_cond_t * cond, char *buf, size_t buf_len) 103 { 104 int ret; 105 106 if ((ret = pthread_cond_init(cond, NULL)) != 0) { 107 tst_brkm(TBROK, cleanup, "pthread_cond_init failed: %s", 108 strerror_r(ret, buf, buf_len)); 109 } 110 } 111 112 void call_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex, 113 char *buf, size_t buf_len) 114 { 115 int ret; 116 117 if ((ret = pthread_cond_wait(cond, mutex)) != 0) { 118 tst_brkm(TBROK, cleanup, "pthread_cond_wait failed: %s", 119 strerror_r(ret, buf, buf_len)); 120 } 121 } 122 123 void call_cond_signal(pthread_cond_t * cond, char *buf, size_t buf_len) 124 { 125 int ret; 126 127 if ((ret = pthread_cond_signal(cond)) != 0) { 128 tst_brkm(TBROK, cleanup, "pthread_cond_signal failed: %s", 129 strerror_r(ret, buf, buf_len)); 130 } 131 } 132 133 void do_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 134 char *buf, size_t buf_len, int i) 135 { 136 struct timeval tv; 137 struct timespec ts; 138 int ret; 139 140 if (gettimeofday(&tv, NULL) != 0) { 141 tst_brkm(TBROK, cleanup, "gettimeofday failed: %s", 142 strerror_r(errno, buf, buf_len)); 143 } 144 145 ts.tv_sec = tv.tv_sec; 146 ts.tv_nsec = (tv.tv_usec * 1000) + NSECS_TO_WAIT; 147 ts.tv_sec += ts.tv_nsec / 1000000000; 148 ts.tv_nsec = ts.tv_nsec % 1000000000; 149 150 call_mutex_lock(mutex, buf, buf_len); 151 if ((ret = pthread_cond_timedwait(cond, mutex, &ts)) != ETIMEDOUT) { 152 #if DEBUG 153 tst_resm(TINFO, 154 "Loop %d of 1000000: pthread_cond_timedwait() didn't timeout", 155 i); 156 tst_resm(TINFO, 157 "You may want to try reducing the value of NSECS_TO_WAIT (currently=%d)", 158 NSECS_TO_WAIT); 159 #endif 160 } 161 call_mutex_unlock(mutex, buf, buf_len); 162 163 } 164 165 void *run(void *arg) 166 { 167 char buf[1024]; 168 169 while (1) { 170 call_mutex_lock(&ack, buf, sizeof(buf)); 171 idle_count = 1; 172 call_cond_signal(&parent, buf, sizeof(buf)); 173 call_mutex_lock(&req, buf, sizeof(buf)); 174 call_mutex_unlock(&ack, buf, sizeof(buf)); 175 176 call_mutex_lock(&wait, buf, sizeof(buf)); 177 call_cond_signal(&parent, buf, sizeof(buf)); 178 call_mutex_unlock(&wait, buf, sizeof(buf)); 179 180 call_cond_wait(&child, &req, buf, sizeof(buf)); 181 call_mutex_unlock(&req, buf, sizeof(buf)); 182 } 183 } 184 185 void create_child_thread(char *buf, size_t buf_len) 186 { 187 pthread_attr_t attr; 188 pthread_t child_tid; 189 int ret; 190 191 if ((ret = pthread_attr_init(&attr)) != 0) { 192 tst_brkm(TBROK, cleanup, "pthread_attr_init failed: %s", 193 strerror_r(ret, buf, buf_len)); 194 } 195 if ((ret = pthread_attr_setdetachstate(&attr, 196 PTHREAD_CREATE_DETACHED)) != 0) { 197 tst_brkm(TBROK, cleanup, 198 "pthread_attr_setdetachstate failed: %s", 199 strerror_r(ret, buf, buf_len)); 200 } 201 if ((ret = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) != 0) { 202 tst_brkm(TBROK, cleanup, "pthread_attr_setscope failed: %s", 203 strerror_r(ret, buf, buf_len)); 204 } 205 206 if ((ret = pthread_create(&child_tid, &attr, run, NULL)) != 0) { 207 tst_brkm(TBROK, cleanup, "pthread_create failed: %s", 208 strerror_r(ret, buf, buf_len)); 209 } 210 } 211 212 void trap_alarm(int sig) 213 { 214 tst_brkm(TFAIL, cleanup, "Test hang longer than %d sec detected", 215 MAXTIME); 216 } 217 218 static void usage(const char *progname) 219 { 220 fprintf(stderr, "usage: %s -l loops\n", progname); 221 fprintf(stderr, "\t-l specify the number of loops to carry out\n"); 222 } 223 224 int main(int argc, char **argv) 225 { 226 #ifdef USING_NPTL 227 char buf[1024]; 228 int i; 229 extern char *optarg; 230 int numloops = NUMLOOPS; 231 232 while ((i = getopt(argc, argv, "l:")) != -1) { 233 switch (i) { 234 case 'l': 235 if (optarg) 236 numloops = atoi(optarg); 237 else 238 fprintf(stderr, 239 "%s: option -l requires an argument\n", 240 argv[0]); 241 break; 242 default: 243 usage(argv[0]); 244 exit(1); 245 } 246 } 247 248 signal(SIGALRM, trap_alarm); 249 alarm(MAXTIME); 250 251 call_mutex_init(&req, buf, sizeof(buf)); 252 call_mutex_init(&ack, buf, sizeof(buf)); 253 call_mutex_init(&wait, buf, sizeof(buf)); 254 call_cond_init(&parent, buf, sizeof(buf)); 255 call_cond_init(&child, buf, sizeof(buf)); 256 257 call_mutex_lock(&ack, buf, sizeof(buf)); 258 259 create_child_thread(buf, sizeof(buf)); 260 261 tst_resm(TINFO, "Starting test, please wait."); 262 for (i = 0; i < numloops; i++) { 263 while (idle_count == 0) { 264 call_cond_wait(&parent, &ack, buf, sizeof(buf)); 265 }; 266 idle_count = 0; 267 call_mutex_unlock(&ack, buf, sizeof(buf)); 268 269 do_timedwait(&parent, &wait, buf, sizeof(buf), i); 270 271 call_mutex_lock(&req, buf, sizeof(buf)); 272 call_cond_signal(&child, buf, sizeof(buf)); 273 call_mutex_unlock(&req, buf, sizeof(buf)); 274 #ifdef DEBUG 275 tst_resm(TINFO, "Success in loop %d", i); 276 #else 277 if (((i % (numloops / 10)) == 0) && (i != 0)) 278 tst_resm(TINFO, "Success thru loop %d of %i", i, 279 numloops); 280 #endif 281 call_mutex_lock(&ack, buf, sizeof(buf)); 282 } 283 284 alarm(0); 285 tst_resm(TPASS, "Test completed successfully!"); 286 cleanup(); 287 288 #else 289 tst_brkm(TCONF, NULL, 290 "Skipping Execution - This system is not using NPTL"); 291 #endif 292 293 return 1; 294 } 295 296 /* 297 *cleanup() - performs all ONE TIME cleanup for this test at 298 * completion or premature exit. 299 */ 300 void cleanup() 301 { 302 303 tst_exit(); 304 } 305