• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
call_mutex_init(pthread_mutex_t * mutex,char * buf,size_t buf_len)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 
call_mutex_lock(pthread_mutex_t * mutex,char * buf,size_t buf_len)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 
call_mutex_unlock(pthread_mutex_t * mutex,char * buf,size_t buf_len)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 
call_cond_init(pthread_cond_t * cond,char * buf,size_t buf_len)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 
call_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex,char * buf,size_t buf_len)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 
call_cond_signal(pthread_cond_t * cond,char * buf,size_t buf_len)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 
do_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,char * buf,size_t buf_len,int i)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 
run(void * arg)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 
create_child_thread(char * buf,size_t buf_len)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 
trap_alarm(int sig)212 void trap_alarm(int sig)
213 {
214 	tst_brkm(TFAIL, cleanup, "Test hang longer than %d sec detected",
215 		 MAXTIME);
216 }
217 
usage(const char * progname)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 
main(int argc,char ** argv)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  */
cleanup()300 void cleanup()
301 {
302 
303 	tst_exit();
304 }
305