Home | History | Annotate | Download | only in nptl
      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