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.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
17 * This file tests the following assertion:
18 *
19 * The pthread_cond_broadcast function unblocks all the threads blocked on the
20 * conditional variable.
21
22 * The steps are:
23 *  -> Create as many threads as possible which will wait on a condition
24 *     variable
25 *  -> broadcast the condition and check that all threads are awaken
26 *
27 *  The test will fail when the threads are not terminated within a certain
28 *  duration.
29 *
30 */
31
32#define _POSIX_C_SOURCE 200112L
33
34#ifndef WITHOUT_XOPEN
35#define _XOPEN_SOURCE	600
36#endif
37
38#include <pthread.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <unistd.h>
43
44#include <errno.h>
45#include <signal.h>
46#include <string.h>
47#include <time.h>
48#include <sys/mman.h>
49#include <sys/wait.h>
50#include <semaphore.h>
51#include <sys/sysinfo.h>
52
53#include "../testfrmw/testfrmw.h"
54#include "../testfrmw/testfrmw.c"
55
56#define UNRESOLVED_KILLALL(error, text) {		\
57	if (td->fork) {					\
58		children_t *tmp;			\
59		while (children->next != NULL) {	\
60			tmp = children->next;		\
61			children->next = tmp->next;	\
62			kill(tmp->data.p, SIGKILL);	\
63			free(tmp);			\
64		}					\
65	}						\
66	UNRESOLVED(error, text);			\
67	}
68#define FAILED_KILLALL(text) {				\
69	if (td->fork) {					\
70		children_t *tmp;			\
71		while (children->next != NULL) {	\
72			tmp = children->next;		\
73			children->next = tmp->next;	\
74			kill(tmp->data.p, SIGKILL);	\
75			free(tmp);			\
76		}					\
77	}						\
78	FAILED(text);					\
79	}
80
81#ifndef VERBOSE
82#define VERBOSE 1
83#endif
84
85/* Do not create more than this amount of children: */
86static int max_process_children = 200;
87static int max_thread_children = 1000;
88
89#define TIMEOUT  (180)
90
91#ifndef WITHOUT_ALTCLK
92#define USE_ALTCLK		/* make tests with MONOTONIC CLOCK if supported */
93#endif
94
95#ifdef WITHOUT_XOPEN
96/* We define those to avoid compilation errors, but they won't be used */
97#define PTHREAD_MUTEX_DEFAULT 0
98#define PTHREAD_MUTEX_NORMAL 0
99#define PTHREAD_MUTEX_ERRORCHECK 0
100#define PTHREAD_MUTEX_RECURSIVE 0
101
102#endif
103
104struct _scenar {
105	int m_type;
106	int mc_pshared;
107	int c_clock;
108	int fork;
109	char *descr;
110} scenarii[] = {
111	{
112	PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}, {
113	PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}, {
114	PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}, {
115	PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}, {
116	PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}, {
117	PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}, {
118	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}, {
119	PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}, {
120	PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
121		    "Pshared default mutex across processes"}, {
122	PTHREAD_MUTEX_NORMAL, 1, 0, 1, "Pshared normal mutex across processes"},
123	{
124	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
125		    "Pshared errorcheck mutex across processes"}, {
126	PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
127		    "Pshared recursive mutex across processes"},
128#ifdef USE_ALTCLK
129	{
130	PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
131		    "Pshared default mutex and alt clock condvar across processes"},
132	{
133	PTHREAD_MUTEX_NORMAL, 1, 1, 1,
134		    "Pshared normal mutex and alt clock condvar across processes"},
135	{
136	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
137		    "Pshared errorcheck mutex and alt clock condvar across processes"},
138	{
139	PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
140		    "Pshared recursive mutex and alt clock condvar across processes"},
141	{
142	PTHREAD_MUTEX_DEFAULT, 0, 1, 0, "Default mutex and alt clock condvar"},
143	{
144	PTHREAD_MUTEX_NORMAL, 0, 1, 0, "Normal mutex and alt clock condvar"},
145	{
146	PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
147		    "Errorcheck mutex and alt clock condvar"}, {
148	PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
149		    "Recursive mutex and alt clock condvar"}, {
150	PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
151		    "PShared default mutex and alt clock condvar"}, {
152	PTHREAD_MUTEX_NORMAL, 1, 1, 0,
153		    "Pshared normal mutex and alt clock condvar"}, {
154	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
155		    "Pshared errorcheck mutex and alt clock condvar"}, {
156	PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
157		    "Pshared recursive mutex and alt clock condvar"},
158#endif
159};
160
161#define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
162
163struct testdata {
164	int count;		/* number of children currently waiting */
165	pthread_cond_t cnd;
166	pthread_mutex_t mtx;
167	int predicate;		/* Boolean associated to the condvar */
168	clockid_t cid;		/* clock used in the condvar */
169	char fork;		/* the children are processes */
170};
171struct testdata *td;
172
173/* Child function (either in a thread or in a process) */
174static void *child(void *arg)
175{
176	int ret = 0;
177	struct timespec ts;
178	char timed;
179
180	/* lock the mutex */
181	ret = pthread_mutex_lock(&td->mtx);
182	if (ret != 0)
183		UNRESOLVED(ret, "Failed to lock mutex in child");
184
185	/* increment count */
186	td->count++;
187
188	timed = td->count & 1;
189
190	if (timed) {
191		/* get current time if we are a timedwait */
192		ret = clock_gettime(td->cid, &ts);
193		if (ret != 0)
194			UNRESOLVED(errno, "Unable to read clock");
195		ts.tv_sec += TIMEOUT;
196	}
197
198	do {
199		/* Wait while the predicate is false */
200		if (timed)
201			ret = pthread_cond_timedwait(&td->cnd, &td->mtx, &ts);
202		else
203			ret = pthread_cond_wait(&td->cnd, &td->mtx);
204#if VERBOSE > 5
205		output("[child] Wokenup timed=%i, Predicate=%i, ret=%i\n",
206		       timed, td->predicate, ret);
207#endif
208	} while ((ret == 0) && (td->predicate == 0));
209	if (ret == ETIMEDOUT) {
210		ret = pthread_mutex_unlock(&td->mtx);
211		if (ret != 0)
212			UNRESOLVED(ret, "Failed to unlock the mutex.");
213		FAILED("Timeout occured. This means a cond signal was lost -- "
214		       "or parent died");
215	}
216	if (ret != 0)
217		UNRESOLVED(ret, "Failed to wait for the cond");
218
219	/* unlock the mutex */
220	ret = pthread_mutex_unlock(&td->mtx);
221	if (ret != 0)
222		UNRESOLVED(ret, "Failed to unlock the mutex.");
223
224	return NULL;
225}
226
227/* Structure used to store the children information */
228typedef struct _children {
229	union {
230		pid_t p;
231		pthread_t t;
232	} data;
233	struct _children *next;
234} children_t;
235
236children_t sentinel = {.next = NULL };
237
238children_t *children = &sentinel;
239
240/* Timeout thread */
241
242sem_t sem_tmr;
243
244void *timer(void *arg)
245{
246	unsigned int to = TIMEOUT;
247	int ret;
248
249	do {
250		ret = sem_wait(&sem_tmr);
251	} while ((ret != 0) && (errno == EINTR));
252	if (ret != 0)
253		UNRESOLVED(errno, "Failed to wait for the semaphore "
254			   "in timer thread.");
255
256	do {
257		to = sleep(to);
258	} while (to > 0);
259	FAILED_KILLALL("Operation timed out. A signal was lost.");
260	return NULL;		/* For compiler */
261}
262
263#ifdef __linux__
264static void children_number(void)
265{
266	struct sysinfo sysinformation;
267	int ret;
268	int avail_number;
269	unsigned long per_process;
270	unsigned long min_stack;
271
272	min_stack = sysconf(_SC_THREAD_STACK_MIN);
273
274	ret = sysinfo(&sysinformation);
275	if (ret != 0)
276		UNRESOLVED(ret, "Failed to get system information.");
277
278	per_process = min_stack * max_thread_children;
279	if (per_process > sysinformation.freeram)
280		UNTESTED("Not enough memory.");
281
282	avail_number = sysinformation.freeram / per_process;
283
284	if (avail_number < 10)
285		UNTESTED("Not enough memory.");
286
287	max_process_children = (avail_number < max_process_children ?
288				avail_number : max_process_children);
289
290	return;
291}
292#else
293static void children_number(void)
294{
295	return;
296}
297#endif
298
299int main(void)
300{
301	int ret;
302
303	pthread_mutexattr_t ma;
304	pthread_condattr_t ca;
305
306	int scenar;
307	long pshared, monotonic, cs, mf;
308
309	int child_count;
310	children_t *tmp, *cur;
311
312	int ch;
313	pid_t pid;
314	int status;
315
316	pthread_t t_timer;
317
318	pthread_attr_t ta;
319
320	struct testdata alternativ;
321
322	output_init();
323
324	children_number();
325
326	/* check the system abilities */
327	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
328	cs = sysconf(_SC_CLOCK_SELECTION);
329	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
330	mf = sysconf(_SC_MAPPED_FILES);
331
332#if VERBOSE > 0
333	output("Test starting\n");
334	output("System abilities:\n");
335	output(" TPS : %li\n", pshared);
336	output(" CS  : %li\n", cs);
337	output(" MON : %li\n", monotonic);
338	output(" MF  : %li\n", mf);
339	if ((mf < 0) || (pshared < 0))
340		output("Process-shared attributes won't be tested\n");
341	if ((cs < 0) || (monotonic < 0))
342		output("Alternative clock won't be tested\n");
343	fflush(stdout);
344#endif
345
346	if (monotonic < 0)
347		cs = -1;
348
349#ifndef USE_ALTCLK
350	if (cs > 0)
351		output("Implementation supports the MONOTONIC CLOCK "
352		       "but option is disabled in test.\n");
353#endif
354
355/*
356 * Allocate space for the testdata structure
357 */
358	if (mf < 0) {
359		/* Cannot mmap a file, we use an alternative method */
360		td = &alternativ;
361		pshared = -1;	/* We won't do this testing anyway */
362#if VERBOSE > 0
363		output("Testdata allocated in the process memory.\n");
364#endif
365	} else {
366		/* We will place the test data in a mmaped file */
367		char filename[] = "/tmp/cond_broadcast-XXXXXX";
368		size_t sz, ps;
369		void *mmaped;
370		int fd;
371		char *tmp;
372
373		/* We now create the temp files */
374		fd = mkstemp(filename);
375		if (fd == -1)
376			UNRESOLVED(errno,
377				   "Temporary file could not be created");
378
379		/* and make sure the file will be deleted when closed */
380		unlink(filename);
381
382#if VERBOSE > 1
383		output("Temp file created (%s).\n", filename);
384#endif
385
386		ps = (size_t) sysconf(_SC_PAGESIZE);
387		sz = ((sizeof(struct testdata) / ps) + 1) * ps;
388
389		tmp = calloc(1, sz);
390		if (tmp == NULL)
391			UNRESOLVED(errno, "Memory allocation failed");
392
393		/* Write the data to the file.  */
394		if (write(fd, tmp, sz) != (ssize_t) sz)
395			UNRESOLVED(sz, "Writting to the file failed");
396
397		free(tmp);
398
399		/* Now we can map the file in memory */
400		mmaped = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED,
401			      fd, 0);
402		if (mmaped == MAP_FAILED)
403			UNRESOLVED(errno, "mmap failed");
404
405		td = (struct testdata *)mmaped;
406
407		/* Our datatest structure is now in shared memory */
408#if VERBOSE > 1
409		output("Testdata allocated in shared memory (%ib).\n",
410		       sizeof(struct testdata));
411#endif
412	}
413
414	/* Initialize the thread attribute object */
415	ret = pthread_attr_init(&ta);
416	if (ret != 0)
417		UNRESOLVED(ret, "[parent] Failed to initialize a thread "
418			   "attribute object");
419	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
420	if (ret != 0)
421		UNRESOLVED(ret, "[parent] Failed to set thread stack size");
422
423	/* Initialize the semaphore for the timer thread */
424	ret = sem_init(&sem_tmr, 0, 0);
425	if (ret != 0)
426		UNRESOLVED(ret, "Failed to initialize the semaphore");
427
428	/* Do the test for each test scenario */
429	for (scenar = 0; scenar < NSCENAR; scenar++) {
430		/* set / reset everything */
431		td->fork = 0;
432		ret = pthread_mutexattr_init(&ma);
433		if (ret != 0)
434			UNRESOLVED(ret, "[parent] Unable to initialize the "
435				   "mutex attribute object");
436		ret = pthread_condattr_init(&ca);
437		if (ret != 0)
438			UNRESOLVED(ret, "[parent] Unable to initialize the "
439				   "cond attribute object");
440
441#ifndef WITHOUT_XOPEN
442		/* Set the mutex type */
443		ret = pthread_mutexattr_settype(&ma, scenarii[scenar].m_type);
444		if (ret != 0)
445			UNRESOLVED(ret, "[parent] Unable to set mutex type");
446#endif
447
448		/* Set the pshared attributes, if supported */
449		if ((pshared > 0) && (scenarii[scenar].mc_pshared != 0)) {
450			ret = pthread_mutexattr_setpshared(&ma,
451							   PTHREAD_PROCESS_SHARED);
452			if (ret != 0)
453				UNRESOLVED(ret, "[parent] Unable to set the "
454					   "mutex process-shared");
455			ret = pthread_condattr_setpshared(&ca,
456							  PTHREAD_PROCESS_SHARED);
457			if (ret != 0)
458				UNRESOLVED(ret, "[parent] Unable to set the "
459					   "cond var process-shared");
460		}
461
462		/* Set the alternative clock, if supported */
463#ifdef USE_ALTCLK
464		if ((cs > 0) && (scenarii[scenar].c_clock != 0)) {
465			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
466			if (ret != 0)
467				UNRESOLVED(ret, "[parent] Unable to set the "
468					   "monotonic clock for the cond");
469		}
470		ret = pthread_condattr_getclock(&ca, &td->cid);
471		if (ret != 0)
472			UNRESOLVED(ret, "Unable to get clock from cond attr");
473#else
474		td->cid = CLOCK_REALTIME;
475#endif
476
477		/* Tell whether the test will be across processes */
478		if ((pshared > 0) && (scenarii[scenar].fork != 0))
479			td->fork = 1;
480
481		/* initialize the condvar */
482		ret = pthread_cond_init(&td->cnd, &ca);
483		if (ret != 0)
484			UNRESOLVED(ret, "Cond init failed");
485
486		/* initialize the mutex */
487		ret = pthread_mutex_init(&td->mtx, &ma);
488		if (ret != 0)
489			UNRESOLVED(ret, "Mutex init failed");
490
491		/* Destroy the attributes */
492		ret = pthread_condattr_destroy(&ca);
493		if (ret != 0)
494			UNRESOLVED(ret,
495				   "Failed to destroy the cond var "
496				   "attribute object");
497
498		ret = pthread_mutexattr_destroy(&ma);
499		if (ret != 0)
500			UNRESOLVED(ret,
501				   "Failed to destroy the mutex "
502				   "attribute object");
503
504#if VERBOSE > 2
505		output("[parent] Starting test %s\n", scenarii[scenar].descr);
506#endif
507
508		td->count = 0;
509		child_count = 0;
510		cur = children;
511
512		/* create the timeout thread */
513		ret = pthread_create(&t_timer, NULL, timer, NULL);
514		if (ret != 0)
515			UNRESOLVED_KILLALL(ret,
516					   "Unable to create timer thread");
517
518		/* Create all the children */
519		if (td->fork == 0) {
520			do {
521				tmp = malloc(sizeof(children_t));
522				if (tmp != NULL) {
523					ret = pthread_create(&(tmp->data.t),
524							     &ta, child, NULL);
525					if (ret != 0) {
526						free(tmp);
527					} else {
528						child_count++;
529						tmp->next = NULL;
530						cur->next = tmp;
531						cur = tmp;
532					}
533				} else {
534					ret = errno;
535				}
536			} while ((ret == 0)
537				 && (child_count < max_thread_children));
538#if VERBOSE > 2
539			output("[parent] Created %i children threads\n",
540			       child_count);
541#endif
542			if (child_count == 0)
543				UNRESOLVED(ret, "Unable to create any thread");
544		} else {
545			do {
546				tmp = malloc(sizeof(children_t));
547				if (tmp != NULL) {
548					tmp->data.p = fork();
549					if (tmp->data.p == 0) {
550						child(NULL);
551						exit(0);
552					}
553					if (tmp->data.p == -1) {
554						ret = errno;
555						free(tmp);
556					} else {
557						ret = 0;
558						child_count++;
559						tmp->next = NULL;
560						cur->next = tmp;
561						cur = tmp;
562					}
563				} else {
564					ret = errno;
565				}
566			} while ((ret == 0)
567				 && (child_count < max_process_children));
568#if VERBOSE > 2
569			output("[parent] Created %i children processes\n",
570			       child_count);
571#endif
572			if (child_count == 0)
573				UNRESOLVED(ret, "Unable to create any process");
574
575		}
576
577		/* Make sure all children are waiting */
578		ret = pthread_mutex_lock(&td->mtx);
579		if (ret != 0)
580			UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
581		ch = td->count;
582		while (ch < child_count) {
583			ret = pthread_mutex_unlock(&td->mtx);
584			if (ret != 0)
585				UNRESOLVED_KILLALL(ret,
586						   "Failed to unlock mutex");
587			sched_yield();
588			ret = pthread_mutex_lock(&td->mtx);
589			if (ret != 0)
590				UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
591			ch = td->count;
592		}
593
594#if VERBOSE > 4
595		output("[parent] All children are waiting\n");
596#endif
597
598		/* start the timer count */
599		do {
600			ret = sem_post(&sem_tmr);
601		} while ((ret != 0) && (errno == EINTR));
602		if (ret != 0)
603			UNRESOLVED_KILLALL(errno,
604					   "Failed to post the semaphore.");
605
606		/* Wakeup the children */
607		td->predicate = 1;
608		ret = pthread_cond_broadcast(&td->cnd);
609		if (ret != 0)
610			UNRESOLVED_KILLALL(ret,
611					   "Failed to broadcast the condition.");
612
613#if VERBOSE > 4
614		output("[parent] Condition was signaled\n");
615#endif
616
617		ret = pthread_mutex_unlock(&td->mtx);
618		if (ret != 0)
619			UNRESOLVED_KILLALL(ret, "Failed to unlock mutex");
620
621#if VERBOSE > 4
622		output("[parent] Joining the children\n");
623#endif
624
625		/* join the children */
626		while (children->next != NULL) {
627			tmp = children->next;
628			children->next = tmp->next;
629			if (td->fork == 0) {
630				ret |= pthread_join(tmp->data.t, NULL);
631			} else {
632				pid = waitpid(tmp->data.p, &status, 0);
633				if (pid != tmp->data.p) {
634					ret = errno;
635					output("Waitpid failed (expected: %i, "
636					       "got: %i)\n", tmp->data.p, pid);
637					free(tmp);
638					UNRESOLVED_KILLALL(ret,
639							   "Waitpid failed");
640				}
641				if (WIFEXITED(status)) {
642					/* the child should return only failed or unresolved or passed */
643					if (ret != PTS_FAIL)
644						ret |= WEXITSTATUS(status);
645				}
646			}
647			free(tmp);
648		}
649		if (ret != 0) {
650			output_fini();
651			exit(ret);
652		}
653#if VERBOSE > 4
654		output("[parent] All children terminated\n");
655#endif
656
657		/* cancel the timeout thread */
658		ret = pthread_cancel(t_timer);
659		if (ret != 0)
660			/* Strange error here... the thread cannot be terminated
661			 * (app would be killed)
662			 */
663			UNRESOLVED(ret, "Failed to cancel the timeout handler");
664
665		/* join the timeout thread */
666		ret = pthread_join(t_timer, NULL);
667		if (ret != 0)
668			UNRESOLVED(ret, "Failed to join the timeout handler");
669
670		/* Destroy the datas */
671		ret = pthread_cond_destroy(&td->cnd);
672		if (ret != 0)
673			UNRESOLVED(ret, "Failed to destroy the condvar");
674
675		ret = pthread_mutex_destroy(&td->mtx);
676		if (ret != 0)
677			UNRESOLVED(ret, "Failed to destroy the mutex");
678
679	}
680
681	/* Destroy global data */
682	ret = pthread_attr_destroy(&ta);
683	if (ret != 0)
684		UNRESOLVED(ret, "Final thread attr destroy failed");
685
686	ret = sem_destroy(&sem_tmr);
687	if (ret != 0)
688		UNRESOLVED(errno, "Final semaphore destroy failed");
689
690	/* exit */
691	PASSED;
692}
693