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 file is a stress test for the function pthread_cond_timedwait.
19  *
20  * It aims to check the following assertion:
21  *  When a cancel request unblocks the thread,
22  *  it must not consume any pending condition signal request.
23 
24  * The steps are:
25  *  -> Create a bunch of threads waiting on a condvar.
26  *  -> At the same time (using a barrier) one thread is canceled and the condition is signaled.
27  *  -> Test checks that the cond signaling was not lost (at least one thread must have woken cleanly).
28  *  -> Then everything is cleaned up and started again.
29 
30  */
31 
32 
33  /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
34  #define _POSIX_C_SOURCE 200112L
35 
36  /* We need the XSI extention for the mutex attributes */
37 #ifndef WITHOUT_XOPEN
38  #define _XOPEN_SOURCE	600
39 #endif
40 /********************************************************************************************/
41 /****************************** standard includes *****************************************/
42 /********************************************************************************************/
43  #include <pthread.h>
44  #include <stdarg.h>
45  #include <stdio.h>
46  #include <stdlib.h>
47  #include <unistd.h>
48 
49  #include <errno.h>
50  #include <signal.h>
51  #include <string.h>
52  #include <time.h>
53 
54 /********************************************************************************************/
55 /******************************   Test framework   *****************************************/
56 /********************************************************************************************/
57  #include "testfrmw.h"
58  #include "testfrmw.c"
59  /* This header is responsible for defining the following macros:
60   * UNRESOLVED(ret, descr);
61   *    where descr is a description of the error and ret is an int (error code for example)
62   * FAILED(descr);
63   *    where descr is a short text saying why the test has failed.
64   * PASSED();
65   *    No parameter.
66   *
67   * Both three macros shall terminate the calling process.
68   * The testcase shall not terminate in any other maneer.
69   *
70   * The other file defines the functions
71   * void output_init()
72   * void output(char * string, ...)
73   *
74   * Those may be used to output information.
75   */
76 
77 /********************************************************************************************/
78 /********************************** Configuration ******************************************/
79 /********************************************************************************************/
80 #ifndef SCALABILITY_FACTOR
81 #define SCALABILITY_FACTOR 1
82 #endif
83 #ifndef VERBOSE
84 #define VERBOSE 1
85 #endif
86 
87 /* Size of the "bunch" of threads -- the real number will be 2 more threads per scenarii */
88 #define NCHILDREN (20)
89 
90 #define TIMEOUT  (60)
91 
92 
93 #ifndef WITHOUT_ALTCLK
94 #define USE_ALTCLK  /* make tests with MONOTONIC CLOCK if supported */
95 #endif
96 
97 /********************************************************************************************/
98 /***********************************    Test case   *****************************************/
99 /********************************************************************************************/
100 
101 #ifdef WITHOUT_XOPEN
102 /* We define those to avoid compilation errors, but they won't be used */
103 #define PTHREAD_MUTEX_DEFAULT 0
104 #define PTHREAD_MUTEX_NORMAL 0
105 #define PTHREAD_MUTEX_ERRORCHECK 0
106 #define PTHREAD_MUTEX_RECURSIVE 0
107 
108 #endif
109 
110 struct _scenar
111 {
112 	int m_type; /* Mutex type to use */
113 	int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */
114 	int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */
115 	int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */
116 	char * descr; /* Case description */
117 }
118 scenarii[] =
119 {
120 	 {PTHREAD_MUTEX_DEFAULT,    0, 0, 0, "Default mutex"}
121 	,{PTHREAD_MUTEX_NORMAL,     0, 0, 0, "Normal mutex"}
122 	,{PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}
123 	,{PTHREAD_MUTEX_RECURSIVE,  0, 0, 0, "Recursive mutex"}
124 
125 	,{PTHREAD_MUTEX_DEFAULT,    1, 0, 0, "PShared default mutex"}
126 	,{PTHREAD_MUTEX_NORMAL,     1, 0, 0, "Pshared normal mutex"}
127 	,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}
128 	,{PTHREAD_MUTEX_RECURSIVE,  1, 0, 0, "Pshared recursive mutex"}
129 
130 	,{PTHREAD_MUTEX_DEFAULT,    1, 0, 1, "Pshared default mutex across processes"}
131 	,{PTHREAD_MUTEX_NORMAL,     1, 0, 1, "Pshared normal mutex across processes"}
132 	,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, "Pshared errorcheck mutex across processes"}
133 	,{PTHREAD_MUTEX_RECURSIVE,  1, 0, 1, "Pshared recursive mutex across processes"}
134 
135 #ifdef USE_ALTCLK
136 	,{PTHREAD_MUTEX_DEFAULT,    1, 1, 1, "Pshared default mutex and alt clock condvar across processes"}
137 	,{PTHREAD_MUTEX_NORMAL,     1, 1, 1, "Pshared normal mutex and alt clock condvar across processes"}
138 	,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, "Pshared errorcheck mutex and alt clock condvar across processes"}
139 	,{PTHREAD_MUTEX_RECURSIVE,  1, 1, 1, "Pshared recursive mutex and alt clock condvar across processes"}
140 
141 	,{PTHREAD_MUTEX_DEFAULT,    0, 1, 0, "Default mutex and alt clock condvar"}
142 	,{PTHREAD_MUTEX_NORMAL,     0, 1, 0, "Normal mutex and alt clock condvar"}
143 	,{PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, "Errorcheck mutex and alt clock condvar"}
144 	,{PTHREAD_MUTEX_RECURSIVE,  0, 1, 0, "Recursive mutex and alt clock condvar"}
145 
146 	,{PTHREAD_MUTEX_DEFAULT,    1, 1, 0, "PShared default mutex and alt clock condvar"}
147 	,{PTHREAD_MUTEX_NORMAL,     1, 1, 0, "Pshared normal mutex and alt clock condvar"}
148 	,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, "Pshared errorcheck mutex and alt clock condvar"}
149 	,{PTHREAD_MUTEX_RECURSIVE,  1, 1, 0, "Pshared recursive mutex and alt clock condvar"}
150 #endif
151 };
152 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
153 
154 
155 /* This is the shared structure for all threads related to the same condvar */
156 struct celldata
157 {
158 	pthread_t workers[NCHILDREN * SCALABILITY_FACTOR + 2];
159 	pthread_t signaler;
160 
161 	pthread_barrier_t bar;
162 	pthread_mutex_t mtx;
163 	pthread_cond_t cnd;
164 	clockid_t cid;
165 
166 	int boolean;
167 	int count;
168 
169 	long canceled;
170 	long cancelfailed;
171 	long cnttotal;
172 } cells[NSCENAR * SCALABILITY_FACTOR];
173 
174 char do_it=1;
175 pthread_attr_t ta;
176 
177 
cleanup(void * arg)178 void cleanup(void * arg)
179 {
180 	int ret;
181 	struct celldata * cd = (struct celldata *) arg;
182 
183 	/* Unlock the mutex */
184 	ret = pthread_mutex_unlock(&(cd->mtx));
185 if (ret != 0)  { UNRESOLVED(ret, "Failed to unlock mutex in cancel handler"); }
186 
187 }
188 
worker(void * arg)189 void * worker(void * arg)
190 {
191 	int ret;
192 	struct celldata * cd = (struct celldata *) arg;
193 	struct timespec ts;
194 
195 	/* lock the mutex */
196 	ret = pthread_mutex_lock(&(cd->mtx));
197 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock mutex in worker");  }
198 
199 	/* Tell the cellmaster we are ready (count++) */
200 	cd->count += 1;
201 
202 	/* Timeout = now + TIMEOUT */
203 	ret = clock_gettime(cd->cid, &ts);
204 	if (ret != 0)  {  UNRESOLVED(errno, "Gettime failed");  }
205 	ts.tv_sec += TIMEOUT * SCALABILITY_FACTOR;
206 
207 	/* register cleanup handler */
208 	pthread_cleanup_push(cleanup, arg);
209 
210 	do
211 	{
212 		/* cond timedwait (while boolean == false)*/
213 		ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts);
214 
215 		/* if timeout => failed (lost signal) */
216 		if (ret == ETIMEDOUT)
217 		{
218 			FAILED("Timeout occured. A condition signal was probably lost.");
219 		}
220 
221 		if (ret != 0)  {  UNRESOLVED(ret, "Cond timedwait failed");  }
222 
223 	} while (cd->boolean == 0);
224 
225 	/* broadcast the condition */
226 	ret = pthread_cond_broadcast(&(cd->cnd));
227 	if (ret != 0)  {  UNRESOLVED(ret, "Broadcasting the condition failed");  }
228 
229 	/* unregister the cleanup */
230 	pthread_cleanup_pop(0);
231 
232 	/* unlock the mutex */
233 	ret = pthread_mutex_unlock(&(cd->mtx));
234 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock the mutex");  }
235 
236 	return NULL;
237 }
238 
signaler(void * arg)239 void * signaler(void * arg)
240 {
241 	int ret;
242 	struct celldata * cd = (struct celldata *) arg;
243 
244 	/* Lock the mutex if required */
245 	if (cd->boolean == -1)
246 	{
247 		ret = pthread_mutex_lock(&(cd->mtx));
248 		if (ret != 0)  {  UNRESOLVED(ret, "mutex lock failed in signaler");  }
249 	}
250 
251 	/* wait the barrier */
252 	ret = pthread_barrier_wait(&(cd->bar));
253 	if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD))  {  UNRESOLVED(ret, "Barrier wait failed");  }
254 
255 	/* signal the cond */
256 	ret = pthread_cond_signal(&(cd->cnd));
257 	if (ret != 0)  {  UNRESOLVED(ret, "Signaling the cond failed");  }
258 
259 	/* Unlock the mutex if required */
260 	if (cd->boolean == -1)
261 	{
262 		ret = pthread_mutex_unlock(&(cd->mtx));
263 		if (ret != 0)  {  UNRESOLVED(ret, "mutex unlock failed in signaler");  }
264 	}
265 
266 	return NULL;
267 }
268 
cellmanager(void * arg)269 void * cellmanager(void * arg)
270 {
271 	int ret, i;
272 	struct celldata * cd = (struct celldata *) arg;
273 	struct timespec ts;
274 	int randval;
275 	void * w_ret;
276 
277 	cd->canceled = 0;
278 	cd->cancelfailed = 0;
279 	cd->cnttotal = 0;
280 
281 	/* while do_it */
282 	while (do_it)
283 	{
284 		/* Initialize some stuff */
285 		cd->boolean = 0;
286 		cd->count = 0;
287 		cd->cnttotal += 1;
288 
289 		/* create the workers */
290 		for (i=0; i< NCHILDREN * SCALABILITY_FACTOR + 2; i++)
291 		{
292 			ret = pthread_create(&(cd->workers[i]), &ta, worker, arg);
293 			if (ret != 0)  {  UNRESOLVED(ret, "Unable to create enough threads");  }
294 		}
295 
296 		/* choose a (pseudo) random thread to cancel */
297 		ret = clock_gettime(cd->cid, &ts);
298 		if (ret != 0)  {  UNRESOLVED(errno, "Failed to read clock");  }
299 		randval = (ts.tv_sec + (ts.tv_nsec >> 10) ) % (NCHILDREN * SCALABILITY_FACTOR + 2);
300 
301 		/* wait for the workers to be ready */
302 		do
303 		{
304 			ret = pthread_mutex_lock(&(cd->mtx));
305 			if (ret != 0)  {  UNRESOLVED(ret, "Mutex lock failed");  }
306 
307 			i = cd->count;
308 
309 			ret = pthread_mutex_unlock(&(cd->mtx));
310 			if (ret != 0)  {  UNRESOLVED(ret, "Mutex unlock failed");  }
311 		} while (i < NCHILDREN * SCALABILITY_FACTOR + 2);
312 
313 		/* Set the boolean ( 1 => no lock in signaler; -1 => lock ) */
314 		cd->boolean = (ts.tv_sec & 1)?-1:1;
315 
316 		/* create the signaler */
317 		ret = pthread_create(&(cd->signaler), &ta, signaler, arg);
318 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to create signaler thread");  }
319 
320 		/* wait the barrier */
321 		ret = pthread_barrier_wait(&(cd->bar));
322 		if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD))  {  UNRESOLVED(ret, "Failed to wait for the barrier");  }
323 
324 		/* cancel the chosen thread */
325 		ret = pthread_cancel(cd->workers[randval]);
326 
327 		 /* it is possible the thread is already terminated -- so we don't stop on error */
328 		if (ret != 0)
329 		{
330 			#if VERBOSE > 2
331 			output("%d\n", randval);
332 			#endif
333 			cd->cancelfailed +=1;
334 		}
335 
336 		/* join every threads */
337 		ret = pthread_join(cd->signaler, NULL);
338 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to join the signaler thread");  }
339 
340 		for (i=0; i< NCHILDREN * SCALABILITY_FACTOR + 2; i++)
341 		{
342 			ret = pthread_join(cd->workers[i], &w_ret);
343 			if (ret != 0)  {  UNRESOLVED(ret, "Unable to join a worker");  }
344 			if (w_ret == PTHREAD_CANCELED)
345 				cd->canceled += 1;
346 		}
347 	}
348 
349 	return NULL;
350 }
351 
sighdl(int sig)352 void sighdl(int sig)
353 {
354 	/* do_it = 0 */
355 	do { do_it = 0; }
356 	while (do_it);
357 }
358 
main(int argc,char * argv[])359 int main (int argc, char * argv[])
360 {
361 	int ret, i, j;
362 	struct sigaction sa;
363 
364 	pthread_mutexattr_t ma;
365 	pthread_condattr_t ca;
366 	clockid_t cid = CLOCK_REALTIME;
367 	long canceled = 0;
368 	long cancelfailed = 0;
369 	long cnttotal = 0;
370 
371 	long pshared, monotonic, cs;
372 
373 	pthread_t mngrs[NSCENAR * SCALABILITY_FACTOR];
374 
375 	output_init();
376 
377 	/* check the system abilities */
378 	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
379 	cs = sysconf(_SC_CLOCK_SELECTION);
380 	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
381 
382 	#if VERBOSE > 0
383 	output("Test starting\n");
384 	output("System abilities:\n");
385 	output(" TPS : %li\n", pshared);
386 	output(" CS  : %li\n", cs);
387 	output(" MON : %li\n", monotonic);
388 	if ((cs < 0) || (monotonic < 0))
389 		output("Alternative clock won't be tested\n");
390 	#endif
391 
392 	if (monotonic < 0)
393 		cs = -1;
394 
395 	#ifndef USE_ALTCLK
396 	if (cs > 0)
397 		output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
398 	#endif
399 
400 
401 	/* Initialize the celldatas according to scenarii */
402 	for ( i=0; i< NSCENAR ; i++)
403 	{
404 		#if VERBOSE > 1
405 		output("[parent] Preparing attributes for: %s\n", scenarii[i].descr);
406 		#ifdef WITHOUT_XOPEN
407 		output("[parent] Mutex attributes DISABLED -> not used\n");
408 		#endif
409 		#endif
410 
411 		/* set / reset everything */
412 		ret = pthread_mutexattr_init(&ma);
413 		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to initialize the mutex attribute object");  }
414 		ret = pthread_condattr_init(&ca);
415 		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to initialize the cond attribute object");  }
416 
417 		#ifndef WITHOUT_XOPEN
418 		/* Set the mutex type */
419 		ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type);
420 		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set mutex type");  }
421 		#if VERBOSE > 1
422 		output("[parent] Mutex type : %i\n", scenarii[i].m_type);
423 		#endif
424 		#endif
425 
426 		/* Set the pshared attributes, if supported */
427 		if ((pshared > 0) && (scenarii[i].mc_pshared != 0))
428 		{
429 			ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
430 			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the mutex process-shared");  }
431 			ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED);
432 			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the cond var process-shared");  }
433 			#if VERBOSE > 1
434 			output("[parent] Mutex & cond are process-shared\n");
435 			#endif
436 		}
437 		#if VERBOSE > 1
438 		else {
439 			output("[parent] Mutex & cond are process-private\n");
440 		}
441 		#endif
442 
443 		/* Set the alternative clock, if supported */
444 		#ifdef USE_ALTCLK
445 		if ((cs > 0) && (scenarii[i].c_clock != 0))
446 		{
447 			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
448 			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the monotonic clock for the cond");  }
449 			#if VERBOSE > 1
450 			output("[parent] Cond uses the Monotonic clock\n");
451 			#endif
452 		}
453 		#if VERBOSE > 1
454 		else {
455 			output("[parent] Cond uses the default clock\n");
456 		}
457 		#endif
458 		ret = pthread_condattr_getclock(&ca, &cid);
459 		if (ret != 0)  {  UNRESOLVED(ret, "Unable to get clock from cond attr");  }
460 		#endif
461 
462 		/* Initialize all the mutex and condvars which uses those attributes */
463 		for (j=0; j < SCALABILITY_FACTOR; j++)
464 		{
465 			cells[i + j * NSCENAR].cid = cid;
466 
467 			/* initialize the condvar */
468 			ret = pthread_cond_init(&(cells[i + j * NSCENAR].cnd), &ca);
469 			if (ret != 0)  {  UNRESOLVED(ret, "Cond init failed");  }
470 
471 			/* initialize the mutex */
472 			ret = pthread_mutex_init(&(cells[i + j * NSCENAR].mtx), &ma);
473 			if (ret != 0)  {  UNRESOLVED(ret, "Mutex init failed");  }
474 
475 			/* initialize the barrier */
476 			ret = pthread_barrier_init(&(cells[i + j * NSCENAR].bar), NULL, 2);
477 			if (ret != 0)  {  UNRESOLVED(ret, "Failed to init barrier");  }
478 		}
479 
480 		ret = pthread_condattr_destroy(&ca);
481 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the cond var attribute object");  }
482 
483 		ret = pthread_mutexattr_destroy(&ma);
484 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the mutex attribute object");  }
485 	}
486 	#if VERBOSE > 1
487 	output("[parent] All condvars & mutex are ready\n");
488 	#endif
489 
490 	/* register the signal handler */
491 	sigemptyset (&sa.sa_mask);
492 	sa.sa_flags = 0;
493 	sa.sa_handler = sighdl;
494 	if ((ret = sigaction (SIGUSR1, &sa, NULL)))
495 	{ UNRESOLVED(ret, "Unable to register signal handler"); }
496 	#if VERBOSE > 1
497 	output("[parent] Signal handler registered\n");
498 	#endif
499 
500 	/* Initialize the thread attribute object */
501 	ret = pthread_attr_init(&ta);
502 	if (ret != 0)  {  UNRESOLVED(ret, "[parent] Failed to initialize a thread attribute object");  }
503 	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
504 	if (ret != 0)  {  UNRESOLVED(ret, "[parent] Failed to set thread stack size");  }
505 
506 	/* create the NSCENAR * SCALABILITY_FACTOR manager threads */
507 	for (i=0; i<NSCENAR * SCALABILITY_FACTOR; i++)
508 	{
509 		ret = pthread_create( &mngrs[i], &ta, cellmanager, &(cells[i]));
510 		/* In case of failure we can exit; the child process will die after a while */
511 		if (ret != 0)  {  UNRESOLVED(ret, "[Parent] Failed to create a thread");  }
512 
513 		#if VERBOSE > 1
514 		if ((i % 4) == 0)
515 			output("[parent] %i manager threads created...\n", i+1);
516 		#endif
517 	}
518 
519 	#if VERBOSE > 1
520 	output("[parent] All %i manager threads are running...\n", NSCENAR * SCALABILITY_FACTOR);
521 	#endif
522 
523 	/* join the manager threads and destroy the cells */
524 	for (i=0; i<NSCENAR * SCALABILITY_FACTOR; i++)
525 	{
526 		ret = pthread_join( mngrs[i], NULL);
527 		if (ret != 0)  {  UNRESOLVED(ret, "[Parent] Failed to join a thread");  }
528 
529 		canceled += cells[i].canceled;
530 		cancelfailed += cells[i].cancelfailed;
531 		cnttotal += cells[i].cnttotal;
532 
533 		ret = pthread_barrier_destroy(&(cells[i].bar));
534 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy a barrier");  }
535 
536 		ret = pthread_cond_destroy(&(cells[i].cnd));
537 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy a cond");  }
538 
539 		ret = pthread_mutex_destroy(&(cells[i].mtx));
540 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy a mutex");  }
541 	}
542 
543 	/* exit */
544 	#if VERBOSE > 0
545 	output("Test passed\n");
546 	output("  Total loops          : %8li\n", cnttotal);
547 	#endif
548 	#if VERBOSE > 1
549 	output("  Failed cancel request: %8li\n", cancelfailed);
550 	output("  Canceled threads     : %8li\n", canceled);
551 	#endif
552 
553 	PASSED;
554 }
555 
556