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 tests the following assertion:
19 *
20 * The pthread_cond_broadcast function unblocks all the threads blocked on the
21 * conditional variable.
22
23 * The steps are:
24 *  -> Create N threads which will wait on a condition variable
25 *  -> broadcast the condition
26 *  -> Every child checks that it owns the mutex (when possible)
27 *
28 */
29
30
31 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
32 #define _POSIX_C_SOURCE 200112L
33
34 /* We need the XSI extention for the mutex attributes */
35#ifndef WITHOUT_XOPEN
36 #define _XOPEN_SOURCE	600
37#endif
38/********************************************************************************************/
39/****************************** standard includes *****************************************/
40/********************************************************************************************/
41 #include <pthread.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46
47 #include <errno.h>
48 #include <signal.h>
49 #include <string.h>
50 #include <time.h>
51 #include <sys/mman.h>
52 #include <sys/wait.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#define UNRESOLVED_KILLALL(error, text) { \
77	if (td->fork) \
78	{ \
79		int _nch; \
80		for (_nch=0; _nch<children.nb; _nch++) \
81			kill(children.ch[_nch].p, SIGKILL); \
82	} \
83	UNRESOLVED(error, text); \
84	}
85#define FAILED_KILLALL(text, Tchild) { \
86	if (td->fork) \
87	{ \
88		int _nch; \
89		for (_nch=0; _nch<children.nb; _nch++) \
90			kill(children.ch[_nch].p, SIGKILL); \
91	} \
92	FAILED(text); \
93	}
94/********************************************************************************************/
95/********************************** Configuration ******************************************/
96/********************************************************************************************/
97#ifndef VERBOSE
98#define VERBOSE 1
99#endif
100
101#define NCHILDREN (20)
102
103#define TIMEOUT  (120)
104
105
106#ifndef WITHOUT_ALTCLK
107#define USE_ALTCLK  /* make tests with MONOTONIC CLOCK if supported */
108#endif
109
110/********************************************************************************************/
111/***********************************    Test case   *****************************************/
112/********************************************************************************************/
113
114#ifdef WITHOUT_XOPEN
115/* We define those to avoid compilation errors, but they won't be used */
116#define PTHREAD_MUTEX_DEFAULT 0
117#define PTHREAD_MUTEX_NORMAL 0
118#define PTHREAD_MUTEX_ERRORCHECK 0
119#define PTHREAD_MUTEX_RECURSIVE 0
120
121#endif
122
123
124struct _scenar
125{
126	int m_type; /* Mutex type to use */
127	int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */
128	int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */
129	int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */
130	char * descr; /* Case description */
131}
132scenarii[] =
133{
134	 {PTHREAD_MUTEX_DEFAULT,    0, 0, 0, "Default mutex"}
135	,{PTHREAD_MUTEX_NORMAL,     0, 0, 0, "Normal mutex"}
136	,{PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}
137	,{PTHREAD_MUTEX_RECURSIVE,  0, 0, 0, "Recursive mutex"}
138
139	,{PTHREAD_MUTEX_DEFAULT,    1, 0, 0, "PShared default mutex"}
140	,{PTHREAD_MUTEX_NORMAL,     1, 0, 0, "Pshared normal mutex"}
141	,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}
142	,{PTHREAD_MUTEX_RECURSIVE,  1, 0, 0, "Pshared recursive mutex"}
143
144	,{PTHREAD_MUTEX_DEFAULT,    1, 0, 1, "Pshared default mutex across processes"}
145	,{PTHREAD_MUTEX_NORMAL,     1, 0, 1, "Pshared normal mutex across processes"}
146	,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1, "Pshared errorcheck mutex across processes"}
147	,{PTHREAD_MUTEX_RECURSIVE,  1, 0, 1, "Pshared recursive mutex across processes"}
148
149#ifdef USE_ALTCLK
150	,{PTHREAD_MUTEX_DEFAULT,    1, 1, 1, "Pshared default mutex and alt clock condvar across processes"}
151	,{PTHREAD_MUTEX_NORMAL,     1, 1, 1, "Pshared normal mutex and alt clock condvar across processes"}
152	,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1, "Pshared errorcheck mutex and alt clock condvar across processes"}
153	,{PTHREAD_MUTEX_RECURSIVE,  1, 1, 1, "Pshared recursive mutex and alt clock condvar across processes"}
154
155	,{PTHREAD_MUTEX_DEFAULT,    0, 1, 0, "Default mutex and alt clock condvar"}
156	,{PTHREAD_MUTEX_NORMAL,     0, 1, 0, "Normal mutex and alt clock condvar"}
157	,{PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0, "Errorcheck mutex and alt clock condvar"}
158	,{PTHREAD_MUTEX_RECURSIVE,  0, 1, 0, "Recursive mutex and alt clock condvar"}
159
160	,{PTHREAD_MUTEX_DEFAULT,    1, 1, 0, "PShared default mutex and alt clock condvar"}
161	,{PTHREAD_MUTEX_NORMAL,     1, 1, 0, "Pshared normal mutex and alt clock condvar"}
162	,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0, "Pshared errorcheck mutex and alt clock condvar"}
163	,{PTHREAD_MUTEX_RECURSIVE,  1, 1, 0, "Pshared recursive mutex and alt clock condvar"}
164#endif
165};
166#define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
167
168/* The shared data */
169typedef struct
170{
171	int 		count;     /* number of children currently waiting */
172	pthread_cond_t 	cnd;
173	pthread_mutex_t mtx;
174	int 		predicate; /* Boolean associated to the condvar */
175	clockid_t 	cid;       /* clock used in the condvar */
176	int		mtype;     /* Type of the mutex */
177	char		fork;      /* the children are processes */
178} testdata_t;
179testdata_t * td;
180
181struct
182{
183	union
184	{
185		pthread_t t;
186		pid_t p;
187	} ch[NCHILDREN];
188	int nb;
189} children;
190
191
192/* Child function (either in a thread or in a process) */
193void * child(void * arg)
194{
195	int ret=0;
196	int timed;
197	struct timespec ts;
198
199	/* lock the mutex */
200	ret = pthread_mutex_lock(&td->mtx);
201	if (ret != 0)  {  UNRESOLVED(ret, "Failed to lock mutex in child");  }
202
203	/* increment count */
204	td->count++;
205	timed=td->count & 1;
206
207	if (timed)
208	{
209	/* get current time if we are a timedwait */
210		ret = clock_gettime(td->cid, &ts);
211		if (ret != 0)  {  UNRESOLVED(errno, "Unable to read clock");  }
212		ts.tv_sec += TIMEOUT;
213	}
214
215	do {
216	/* Wait while the predicate is false */
217		if (timed)
218			ret = pthread_cond_timedwait(&td->cnd, &td->mtx, &ts);
219		else
220			ret = pthread_cond_wait(&td->cnd, &td->mtx);
221		#if VERBOSE > 5
222		output("[child] Wokenup timed=%i, Predicate=%i, ret=%i\n", timed, td->predicate, ret);
223		#endif
224	} while ((ret == 0) && (td->predicate==0));
225	if (ret == ETIMEDOUT)
226	{
227		FAILED("Timeout occured. This means a cond signal was lost -- or parent died");
228	}
229	if (ret != 0)  {  UNRESOLVED(ret, "Failed to wait for the cond");  }
230
231	/* Check that we are owning the mutex */
232	#ifdef WITHOUT_XOPEN
233	ret = pthread_mutex_trylock(&(td->mtx));
234	if (ret == 0)  {  FAILED("The mutex was not owned after return from condition waiting");  }
235	#else
236	if (td->mtype == PTHREAD_MUTEX_RECURSIVE)
237	{
238		ret = pthread_mutex_trylock(&(td->mtx));
239		if (ret != 0)  {  FAILED("Unable to relock recursive mutex: not owning?");  }
240		ret = pthread_mutex_unlock(&(td->mtx));
241		if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex");  }
242	}
243	if (td->mtype == PTHREAD_MUTEX_ERRORCHECK)
244	{
245		ret = pthread_mutex_lock(&(td->mtx));
246		if (ret == 0)  {  FAILED("Was able to lock errorcheck mutex: the mutex was not acquired once already?");  }
247	}
248	#endif
249
250
251	/* unlock the mutex */
252	ret = pthread_mutex_unlock(&td->mtx);
253	if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex.");  }
254
255	return NULL;
256}
257
258/* Timeout thread */
259void * timer(void * arg)
260{
261	unsigned int to = TIMEOUT;
262	do { to = sleep(to); }
263	while (to>0);
264	FAILED_KILLALL("Operation timed out. A signal was lost.", pchildren);
265	return NULL; /* For compiler */
266}
267
268/* main function */
269
270int main (int argc, char * argv[])
271{
272	int ret;
273
274	pthread_mutexattr_t ma;
275	pthread_condattr_t ca;
276
277	int scenar;
278	long pshared, monotonic, cs, mf;
279
280	int child_count;
281
282	pid_t pid;
283	int status;
284
285	pthread_t t_timer;
286
287	pthread_attr_t ta;
288
289	testdata_t alternativ;
290
291	output_init();
292
293	/* check the system abilities */
294	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
295	cs = sysconf(_SC_CLOCK_SELECTION);
296	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
297	mf =sysconf(_SC_MAPPED_FILES);
298
299	#if VERBOSE > 0
300	output("Test starting\n");
301	output("System abilities:\n");
302	output(" TPS : %li\n", pshared);
303	output(" CS  : %li\n", cs);
304	output(" MON : %li\n", monotonic);
305	output(" MF  : %li\n", mf);
306	if ((mf < 0) || (pshared < 0))
307		output("Process-shared attributes won't be tested\n");
308	if ((cs < 0) || (monotonic < 0))
309		output("Alternative clock won't be tested\n");
310	#endif
311
312	/* We are not interested in testing the clock if we have no other clock available.. */
313	if (monotonic < 0)
314		cs = -1;
315
316#ifndef USE_ALTCLK
317	if (cs > 0)
318		output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
319#endif
320
321/**********
322 * Allocate space for the testdata structure
323 */
324	if (mf < 0)
325	{
326		/* Cannot mmap a file, we use an alternative method */
327		td = &alternativ;
328		pshared = -1; /* We won't do this testing anyway */
329		#if VERBOSE > 0
330		output("Testdata allocated in the process memory.\n");
331		#endif
332	}
333	else
334	{
335		/* We will place the test data in a mmaped file */
336		char filename[] = "/tmp/cond_broadcast-XXXXXX";
337		size_t sz, ps;
338		void * mmaped;
339		int fd;
340		char * tmp;
341
342		/* We now create the temp files */
343		fd = mkstemp(filename);
344		if (fd == -1)
345		{ UNRESOLVED(errno, "Temporary file could not be created"); }
346
347		/* and make sure the file will be deleted when closed */
348		unlink(filename);
349
350		#if VERBOSE > 1
351		output("Temp file created (%s).\n", filename);
352		#endif
353
354		ps = (size_t)sysconf(_SC_PAGESIZE);
355		sz= ((sizeof(testdata_t) / ps) + 1) * ps; /* # pages needed to store the testdata */
356
357		tmp = calloc( 1 , sz);
358		if (tmp == NULL)
359		{ UNRESOLVED(errno, "Memory allocation failed"); }
360
361		/* Write the data to the file.  */
362		if (write (fd, tmp, sz) != (ssize_t) sz)
363		{ UNRESOLVED(sz, "Writting to the file failed"); }
364
365		free(tmp);
366
367		/* Now we can map the file in memory */
368		mmaped = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
369		if (mmaped == MAP_FAILED)
370		{ UNRESOLVED(errno, "mmap failed"); }
371
372		td = (testdata_t *) mmaped;
373
374		/* Our datatest structure is now in shared memory */
375		#if VERBOSE > 1
376		output("Testdata allocated in shared memory (%ib).\n", sizeof(testdata_t));
377		#endif
378	}
379
380	/* Initialize the thread attribute object */
381	ret = pthread_attr_init(&ta);
382	if (ret != 0)  {  UNRESOLVED(ret, "[parent] Failed to initialize a thread attribute object");  }
383	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
384	if (ret != 0)  {  UNRESOLVED(ret, "[parent] Failed to set thread stack size");  }
385
386	/* Do the test for each test scenario */
387	for (scenar=0; scenar < NSCENAR; scenar++)
388	{
389		/* set / reset everything */
390		td->fork=0;
391		ret = pthread_mutexattr_init(&ma);
392		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to initialize the mutex attribute object");  }
393		ret = pthread_condattr_init(&ca);
394		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to initialize the cond attribute object");  }
395
396		#ifndef WITHOUT_XOPEN
397		/* Set the mutex type */
398		ret = pthread_mutexattr_settype(&ma, scenarii[scenar].m_type);
399		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set mutex type");  }
400		#endif
401
402		td->mtype=scenarii[scenar].m_type;
403
404
405		/* Set the pshared attributes, if supported */
406		if ((pshared > 0) && (scenarii[scenar].mc_pshared != 0))
407		{
408			ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
409			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the mutex process-shared");  }
410			ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED);
411			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the cond var process-shared");  }
412		}
413
414		/* Set the alternative clock, if supported */
415		#ifdef USE_ALTCLK
416		if ((cs > 0) && (scenarii[scenar].c_clock != 0))
417		{
418			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
419			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the monotonic clock for the cond");  }
420		}
421		ret = pthread_condattr_getclock(&ca, &td->cid);
422		if (ret != 0)  {  UNRESOLVED(ret, "Unable to get clock from cond attr");  }
423		#else
424		td->cid = CLOCK_REALTIME;
425		#endif
426
427		/* Tell whether the test will be across processes */
428		if ((pshared > 0) && (scenarii[scenar].fork != 0))
429		{
430			td->fork = 1;
431		}
432
433		/* initialize the condvar */
434		ret = pthread_cond_init(&td->cnd, &ca);
435		if (ret != 0)  {  UNRESOLVED(ret, "Cond init failed");  }
436
437		/* initialize the mutex */
438		ret = pthread_mutex_init(&td->mtx, &ma);
439		if (ret != 0)  {  UNRESOLVED(ret, "Mutex init failed");  }
440
441		/* Destroy the attributes */
442		ret = pthread_condattr_destroy(&ca);
443		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the cond var attribute object");  }
444
445		ret = pthread_mutexattr_destroy(&ma);
446		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the mutex attribute object");  }
447
448		#if VERBOSE > 2
449		output("[parent] Starting test %s\n", scenarii[scenar].descr);
450		#endif
451
452		td->count=0;
453
454		/* Create all the children */
455		for (children.nb=0; children.nb<NCHILDREN; children.nb++)
456		{
457			if (td->fork==0)
458			{
459				ret = pthread_create(&(children.ch[children.nb].t), &ta, child, NULL);
460				if (ret != 0)  {  UNRESOLVED(ret, "Failed to create enough threads");  }
461			}
462			else
463			{
464				children.ch[children.nb].p=fork();
465				if (children.ch[children.nb].p == 0) /* We are the child */
466				{
467					child(NULL);
468					exit(0);
469				}
470				if (children.ch[children.nb].p == -1) /* The fork failed */
471				{
472					children.nb--;
473					UNRESOLVED_KILLALL(errno, "Failed to create enough processes");
474				}
475			}
476		}
477		#if VERBOSE > 4
478		output("[parent] Created %i children\n", NCHILDREN);
479		#endif
480
481		/* Make sure all children are waiting */
482		ret = pthread_mutex_lock(&td->mtx);
483		if (ret != 0) {  UNRESOLVED_KILLALL(ret, "Failed to lock mutex");  }
484		child_count = td->count;
485		while (child_count < NCHILDREN)
486		{
487			ret = pthread_mutex_unlock(&td->mtx);
488			if (ret != 0)  {  UNRESOLVED_KILLALL(ret, "Failed to unlock mutex");  }
489			sched_yield();
490			ret = pthread_mutex_lock(&td->mtx);
491			if (ret != 0) {  UNRESOLVED_KILLALL(ret, "Failed to lock mutex");  }
492			child_count = td->count;
493		}
494
495		#if VERBOSE > 4
496		output("[parent] All children are waiting\n");
497		#endif
498
499		/* create the timeout thread */
500		ret = pthread_create(&t_timer, NULL, timer, NULL);
501		if (ret != 0)  {  UNRESOLVED_KILLALL(ret, "Unable to create timer thread");  }
502
503		/* Wakeup the children */
504		td->predicate=1;
505		ret = pthread_cond_broadcast(&td->cnd);
506		if (ret != 0)  {  UNRESOLVED_KILLALL(ret, "Failed to broadcast the condition.");  }
507
508		#if VERBOSE > 4
509		output("[parent] Condition was signaled\n");
510		#endif
511
512		ret = pthread_mutex_unlock(&td->mtx);
513		if (ret != 0)  {  UNRESOLVED_KILLALL(ret, "Failed to unlock mutex");  }
514
515		#if VERBOSE > 4
516		output("[parent] Joining the children\n");
517		#endif
518
519		/* join the children */
520		for (--children.nb; children.nb >= 0; children.nb--)
521		{
522			if (td->fork==0)
523			{
524				ret = pthread_join(children.ch[children.nb].t, NULL);
525				if (ret != 0)  {  UNRESOLVED(ret, "Failed to join a child thread");  }
526			}
527			else
528			{
529				pid = waitpid(children.ch[children.nb].p, &status, 0);
530				if (pid != children.ch[children.nb].p)
531				{
532					ret = errno;
533					output("Waitpid failed (expected: %i, got: %i)\n", children.ch[children.nb].p, pid);
534					UNRESOLVED_KILLALL(ret, "Waitpid failed");
535				}
536				if (WIFEXITED(status))
537				{
538					/* the child should return only failed or unresolved or passed */
539					if (ret != PTS_FAIL)
540						ret |= WEXITSTATUS(status);
541				}
542			}
543		}
544		if (ret != 0)
545		{
546			output_fini();
547			exit(ret);
548		}
549		#if VERBOSE > 4
550		output("[parent] All children terminated\n");
551		#endif
552
553
554		/* cancel the timeout thread */
555		ret = pthread_cancel(t_timer);
556		if (ret != 0)
557		{
558			/* Strange error here... the thread cannot be terminated (app would be killed) */
559			UNRESOLVED(ret, "Failed to cancel the timeout handler");
560		}
561
562		/* join the timeout thread */
563		ret = pthread_join(t_timer, NULL);
564		if (ret != 0)  {  UNRESOLVED(ret, "Failed to join the timeout handler");  }
565
566		/* Destroy the datas */
567		ret = pthread_cond_destroy(&td->cnd);
568		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the condvar");  }
569
570		ret = pthread_mutex_destroy(&td->mtx);
571		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the mutex");  }
572
573	}
574
575	/* Destroy global data */
576	ret = pthread_attr_destroy(&ta);
577	if (ret != 0)  {  UNRESOLVED(ret, "Final thread attr destroy failed");  }
578
579
580	/* exit */
581	PASSED;
582}
583
584