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
110struct _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}
118scenarii[] =
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 */
156struct 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
174char do_it=1;
175pthread_attr_t ta;
176
177
178void 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));
185if (ret != 0)  { UNRESOLVED(ret, "Failed to unlock mutex in cancel handler"); }
186
187}
188
189void * 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
239void * 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
269void * 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
352void sighdl(int sig)
353{
354	/* do_it = 0 */
355	do { do_it = 0; }
356	while (do_it);
357}
358
359int 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