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 sample test aims to check the following assertion:
18 *
19 * When the function returns successfully, everything is as if
20 * the thread had locked the mutex.
21 *
22
23 * The steps are:
24 * -> For each mutex type;
25 *   -> with and without process-shared primitive if this is supported;
26 *   -> with different clocks if this is supported,
27 * -> Initialize a condvar and a mutex.
28 * -> Create a new thread (or process for process-shared condvars & mutex)
29 * -> The new thread (process) locks the mutex, then waits for the condition .
30 * -> The parent thread (process) then locks the mutex, ensures that the child is waiting,
31 *    then signals the condition; and checks the child does not leave the wait function.
32 * -> The parent unlocks the mutex then joins the child.
33 * -> The child checks that it owns the mutex; then it leaves.
34 */
35
36 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
37#define _POSIX_C_SOURCE 200112L
38
39 /* We need the XSI extention for the mutex attributes
40    and the mkstemp() routine */
41#ifndef WITHOUT_XOPEN
42#define _XOPEN_SOURCE	600
43#endif
44 /********************************************************************************************/
45/****************************** standard includes *****************************************/
46/********************************************************************************************/
47#include <pthread.h>
48#include <stdarg.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <unistd.h>
52
53#include <errno.h>
54#include <sys/wait.h>
55#include <sys/mman.h>
56#include <string.h>
57
58/********************************************************************************************/
59/******************************   Test framework   *****************************************/
60/********************************************************************************************/
61#include "../testfrmw/testfrmw.h"
62#include "../testfrmw/testfrmw.c"
63 /* This header is responsible for defining the following macros:
64  * UNRESOLVED(ret, descr);
65  *    where descr is a description of the error and ret is an int (error code for example)
66  * FAILED(descr);
67  *    where descr is a short text saying why the test has failed.
68  * PASSED();
69  *    No parameter.
70  *
71  * Both three macros shall terminate the calling process.
72  * The testcase shall not terminate in any other maneer.
73  *
74  * The other file defines the functions
75  * void output_init()
76  * void output(char * string, ...)
77  *
78  * Those may be used to output information.
79  */
80
81/********************************************************************************************/
82/********************************** Configuration ******************************************/
83/********************************************************************************************/
84#ifndef VERBOSE
85#define VERBOSE 1
86#endif
87
88#ifndef WITHOUT_ALTCLK
89#define USE_ALTCLK		/* make tests with MONOTONIC CLOCK if supported */
90#endif
91
92/********************************************************************************************/
93/***********************************    Test case   *****************************************/
94/********************************************************************************************/
95#ifndef WITHOUT_XOPEN
96
97typedef struct {
98	pthread_mutex_t mtx;
99	pthread_cond_t cnd;
100	clockid_t cid;		/* Clock id used by the cond var */
101	int type;		/* Mutex type */
102	int ctrl;		/* checkpoints */
103	int bool;		/* Boolean predicate for the condition */
104	int status;		/* error code */
105} testdata_t;
106
107struct _scenar {
108	int m_type;		/* Mutex type to use */
109	int mc_pshared;		/* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */
110	int c_clock;		/* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */
111	int fork;		/* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */
112	char *descr;		/* Case description */
113} scenarii[] = {
114	{
115	PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}
116	, {
117	PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}
118	, {
119	PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}
120	, {
121	PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}
122
123	, {
124	PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}
125	, {
126	PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}
127	, {
128	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}
129	, {
130	PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}
131
132	, {
133	PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
134		    "Pshared default mutex across processes"}
135	, {
136	PTHREAD_MUTEX_NORMAL, 1, 0, 1,
137		    "Pshared normal mutex across processes"}
138	, {
139	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
140		    "Pshared errorcheck mutex across processes"}
141	, {
142	PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
143		    "Pshared recursive mutex across processes"}
144
145#ifdef USE_ALTCLK
146	, {
147	PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
148		    "Pshared default mutex and alt clock condvar across processes"}
149	, {
150	PTHREAD_MUTEX_NORMAL, 1, 1, 1,
151		    "Pshared normal mutex and alt clock condvar across processes"}
152	, {
153	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
154		    "Pshared errorcheck mutex and alt clock condvar across processes"}
155	, {
156	PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
157		    "Pshared recursive mutex and alt clock condvar across processes"}
158
159	, {
160	PTHREAD_MUTEX_DEFAULT, 0, 1, 0,
161		    "Default mutex and alt clock condvar"}
162	, {
163	PTHREAD_MUTEX_NORMAL, 0, 1, 0,
164		    "Normal mutex and alt clock condvar"}
165	, {
166	PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
167		    "Errorcheck mutex and alt clock condvar"}
168	, {
169	PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
170		    "Recursive mutex and alt clock condvar"}
171
172	, {
173	PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
174		    "PShared default mutex and alt clock condvar"}
175	, {
176	PTHREAD_MUTEX_NORMAL, 1, 1, 0,
177		    "Pshared normal mutex and alt clock condvar"}
178	, {
179	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
180		    "Pshared errorcheck mutex and alt clock condvar"}
181	, {
182	PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
183		    "Pshared recursive mutex and alt clock condvar"}
184#endif
185};
186
187void *tf(void *arg)
188{
189	int ret = 0;
190
191	testdata_t *td = (testdata_t *) arg;
192
193	/* Lock the mutex */
194	ret = pthread_mutex_lock(&(td->mtx));
195	if (ret != 0) {
196		td->status = ret;
197		UNRESOLVED(ret, "[child] Unable to lock the mutex");
198	}
199
200	/* Tell the parent the mutex is locked */
201	td->ctrl = 1;
202
203	/* Enter the wait */
204	do {
205		ret = pthread_cond_wait(&(td->cnd), &(td->mtx));
206		td->ctrl = 2;
207	} while ((ret == 0) && (td->bool == 0));
208
209	td->ctrl = 3;
210
211	if (ret != 0) {
212		td->status = ret;
213		UNRESOLVED(ret, "[child] Cond wait returned an error");
214	}
215
216	/* Make sure we are owning the mutex */
217	ret = pthread_mutex_trylock(&(td->mtx));
218	if (td->type == PTHREAD_MUTEX_RECURSIVE) {
219#if VERBOSE > 1
220		output
221		    ("[child] Recursive mutex. Test if we are able to re-lock.\n");
222#endif
223		if (ret != 0) {
224			td->status = ret;
225			FAILED("[child] Unable to relock the recursive mutex");
226		}
227		ret = pthread_mutex_unlock(&(td->mtx));
228		if (ret != 0) {
229			td->status = ret;
230			UNRESOLVED(ret, "[child] Failed to unlock the mutex");
231		}
232	} else {		/* This was not a recursive mutex; the call must have failed */
233
234		if (ret == 0) {
235			td->status = -1;
236			FAILED
237			    ("[child] Thread did not owned the mutex after the wait return.");
238		}
239		if (ret != EBUSY) {
240			td->status = ret;
241			UNRESOLVED(ret,
242				   "[child] Mutex trylock did not return EBUSY");
243		}
244#if VERBOSE > 1
245		output("[child] The mutex was busy (normal).\n");
246#endif
247	}
248
249	ret = pthread_mutex_unlock(&(td->mtx));
250	if (ret != 0) {
251		td->status = ret;
252		output("[child] Got error %i: %s\n", ret, strerror(ret));
253		FAILED
254		    ("[child] Failed to unlock the mutex - owned by another thread?");
255	}
256
257	td->ctrl = 4;
258	return NULL;
259}
260
261int main(void)
262{
263	int ret, i;
264	pthread_mutexattr_t ma;
265	pthread_condattr_t ca;
266
267	testdata_t *td;
268	testdata_t alternativ;
269
270	int do_fork;
271
272	pid_t child_pr = 0, chkpid;
273	int status;
274	pthread_t child_th;
275
276	long pshared, monotonic, cs, mf;
277
278	output_init();
279	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
280	cs = sysconf(_SC_CLOCK_SELECTION);
281	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
282	mf = sysconf(_SC_MAPPED_FILES);
283
284#if VERBOSE > 0
285	output("Test starting\n");
286	output("System abilities:\n");
287	output(" TPS : %li\n", pshared);
288	output(" CS  : %li\n", cs);
289	output(" MON : %li\n", monotonic);
290	output(" MF  : %li\n", mf);
291	if ((mf < 0) || (pshared < 0))
292		output("Process-shared attributes won't be tested\n");
293	if ((cs < 0) || (monotonic < 0))
294		output("Alternative clock won't be tested\n");
295#endif
296
297	/* We are not interested in testing the clock if we have no other clock available.. */
298	if (monotonic < 0)
299		cs = -1;
300
301#ifndef USE_ALTCLK
302	if (cs > 0)
303		output
304		    ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
305#endif
306
307/**********
308 * Allocate space for the testdata structure
309 */
310	if (mf < 0) {
311		/* Cannot mmap a file, we use an alternative method */
312		td = &alternativ;
313		pshared = -1;	/* We won't do this testing anyway */
314#if VERBOSE > 0
315		output("Testdata allocated in the process memory.\n");
316#endif
317	} else {
318		/* We will place the test data in a mmaped file */
319		char filename[] = "/tmp/cond_wait_2-2-XXXXXX";
320		size_t sz;
321		void *mmaped;
322		int fd;
323		char *tmp;
324
325		/* We now create the temp files */
326		fd = mkstemp(filename);
327		if (fd == -1) {
328			UNRESOLVED(errno,
329				   "Temporary file could not be created");
330		}
331
332		/* and make sure the file will be deleted when closed */
333		unlink(filename);
334
335#if VERBOSE > 1
336		output("Temp file created (%s).\n", filename);
337#endif
338
339		sz = (size_t) sysconf(_SC_PAGESIZE);
340
341		tmp = calloc(1, sz);
342		if (tmp == NULL) {
343			UNRESOLVED(errno, "Memory allocation failed");
344		}
345
346		/* Write the data to the file.  */
347		if (write(fd, tmp, sz) != (ssize_t) sz) {
348			UNRESOLVED(sz, "Writting to the file failed");
349		}
350
351		free(tmp);
352
353		/* Now we can map the file in memory */
354		mmaped =
355		    mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
356		if (mmaped == MAP_FAILED) {
357			UNRESOLVED(errno, "mmap failed");
358		}
359
360		td = (testdata_t *) mmaped;
361
362		/* Our datatest structure is now in shared memory */
363#if VERBOSE > 1
364		output("Testdata allocated in shared memory.\n");
365#endif
366	}
367
368/**********
369 * For each test scenario, initialize the attributes and other variables.
370 */
371	for (i = 0; i < (sizeof(scenarii) / sizeof(scenarii[0])); i++) {
372#if VERBOSE > 1
373		output("[parent] Preparing attributes for: %s\n",
374		       scenarii[i].descr);
375#endif
376		/* set / reset everything */
377		do_fork = 0;
378		ret = pthread_mutexattr_init(&ma);
379		if (ret != 0) {
380			UNRESOLVED(ret,
381				   "[parent] Unable to initialize the mutex attribute object");
382		}
383		ret = pthread_condattr_init(&ca);
384		if (ret != 0) {
385			UNRESOLVED(ret,
386				   "[parent] Unable to initialize the cond attribute object");
387		}
388
389		/* Set the mutex type */
390		ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type);
391		if (ret != 0) {
392			UNRESOLVED(ret, "[parent] Unable to set mutex type");
393		}
394#if VERBOSE > 1
395		output("[parent] Mutex type : %i\n", scenarii[i].m_type);
396#endif
397
398		/* Set the pshared attributes, if supported */
399		if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) {
400			ret =
401			    pthread_mutexattr_setpshared(&ma,
402							 PTHREAD_PROCESS_SHARED);
403			if (ret != 0) {
404				UNRESOLVED(ret,
405					   "[parent] Unable to set the mutex process-shared");
406			}
407			ret =
408			    pthread_condattr_setpshared(&ca,
409							PTHREAD_PROCESS_SHARED);
410			if (ret != 0) {
411				UNRESOLVED(ret,
412					   "[parent] Unable to set the cond var process-shared");
413			}
414#if VERBOSE > 1
415			output("[parent] Mutex & cond are process-shared\n");
416#endif
417		}
418#if VERBOSE > 1
419		else {
420			output("[parent] Mutex & cond are process-private\n");
421		}
422#endif
423
424		/* Set the alternative clock, if supported */
425#ifdef USE_ALTCLK
426		if ((cs > 0) && (scenarii[i].c_clock != 0)) {
427			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
428			if (ret != 0) {
429				UNRESOLVED(ret,
430					   "[parent] Unable to set the monotonic clock for the cond");
431			}
432#if VERBOSE > 1
433			output("[parent] Cond uses the Monotonic clock\n");
434#endif
435		}
436#if VERBOSE > 1
437		else {
438			output("[parent] Cond uses the default clock\n");
439		}
440#endif
441#endif
442
443		/* Tell whether the test will be across processes */
444		if ((pshared > 0) && (scenarii[i].fork != 0)) {
445			do_fork = 1;
446#if VERBOSE > 1
447			output("[parent] Child will be a new process\n");
448#endif
449		}
450#if VERBOSE > 1
451		else {
452			output("[parent] Child will be a new thread\n");
453		}
454#endif
455
456/**********
457 * Initialize the testdata_t structure with the previously defined attributes
458 */
459		/* Initialize the mutex */
460		ret = pthread_mutex_init(&(td->mtx), &ma);
461		if (ret != 0) {
462			UNRESOLVED(ret, "[parent] Mutex init failed");
463		}
464
465		/* initialize the condvar */
466		ret = pthread_cond_init(&(td->cnd), &ca);
467		if (ret != 0) {
468			UNRESOLVED(ret, "[parent] Cond init failed");
469		}
470
471		/* Initialize the other datas from the test structure */
472#ifdef USE_ALTCLK
473		ret = pthread_condattr_getclock(&ca, &(td->cid));
474		if (ret != 0) {
475			UNRESOLVED(ret,
476				   "[parent] Unable to read cond clock attribute");
477		}
478#else
479		td->cid = CLOCK_REALTIME;
480#endif
481
482		ret = pthread_mutexattr_gettype(&ma, &(td->type));
483		if (ret != 0) {
484			UNRESOLVED(ret,
485				   "[parent] Unable to read mutex type attribute");
486		}
487
488		td->ctrl = 0;
489		td->bool = 0;
490		td->status = 0;
491
492/**********
493 * Proceed to the actual testing
494 */
495
496		/* Create the child */
497		if (do_fork != 0) {
498			/* We are testing across two processes */
499			child_pr = fork();
500			if (child_pr == -1) {
501				UNRESOLVED(errno, "[parent] Fork failed");
502			}
503
504			if (child_pr == 0) {
505#if VERBOSE > 1
506				output("[child] Child process starting...\n");
507#endif
508
509				if (tf((void *)td) != NULL) {
510					UNRESOLVED(-1,
511						   "[child] Got an unexpected return value from test function");
512				} else {
513					/* We cannot use the PASSED macro here since it would terminate the output */
514					exit(0);
515				}
516			}
517			/* Only the parent process goes further */
518		} else {	/* do_fork == 0 */
519
520			/* We are testing across two threads */
521			ret = pthread_create(&child_th, NULL, tf, td);
522			if (ret != 0) {
523				UNRESOLVED(ret,
524					   "[parent] Unable to create the child thread.");
525			}
526		}
527
528		/* Note: in case of an error, the child process will be alive for 10 sec then exit. */
529
530		/* Child is now running and will enter the timedwait */
531		/* We are waiting for this; and we have to monitor the status value as well. */
532		ret = pthread_mutex_lock(&(td->mtx));
533		if (ret != 0) {
534			UNRESOLVED(ret, "[parent] Unable to lock the mutex");
535		}
536
537		while ((td->ctrl == 0) && (td->status == 0)) {
538			ret = pthread_mutex_unlock(&(td->mtx));
539			if (ret != 0) {
540				UNRESOLVED(ret,
541					   "[parent] Unable to unlock the mutex");
542			}
543			sched_yield();
544			ret = pthread_mutex_lock(&(td->mtx));
545			if (ret != 0) {
546				UNRESOLVED(ret,
547					   "[parent] Unable to lock the mutex");
548			}
549		}
550
551		if ((td->ctrl == 2) && (td->status == 0)) {	/* Spurious wakeups hapenned */
552			output
553			    ("Spurious wake ups have happened. Maybe pthread_cond_wait is broken?\n");
554			td->ctrl = 1;
555		}
556
557		if (td->ctrl == 1) {	/* The child is inside the cond wait */
558			ret = pthread_cond_signal(&(td->cnd));
559			if (ret != 0) {
560				UNRESOLVED(ret,
561					   "[parent] Unable to signal the condition");
562			}
563
564			/* Let the child leave the wait function if something is broken */
565			usleep(100);
566
567			if (td->ctrl != 1) {
568				FAILED
569				    ("[parent] Child went out from pthread_cond_wait without locking the mutex");
570			}
571
572			/* Allow the child to continue */
573			td->bool = 1;
574		}
575
576		/* Let the child do its checking */
577		ret = pthread_mutex_unlock(&(td->mtx));
578		if (ret != 0) {
579			UNRESOLVED(ret, "[parent] Unable to unlock the mutex");
580		}
581
582		/* Wait for the child to terminate */
583		if (do_fork != 0) {
584			/* We were testing across two processes */
585			chkpid = waitpid(child_pr, &status, 0);
586			if (chkpid != child_pr) {
587				output("Expected pid: %i. Got %i\n",
588				       (int)child_pr, (int)chkpid);
589				UNRESOLVED(errno, "Waitpid failed");
590			}
591			if (WIFSIGNALED(status)) {
592				output("Child process killed with signal %d\n",
593				       WTERMSIG(status));
594				UNRESOLVED(td->status,
595					   "Child process was killed");
596			}
597
598			if (WIFEXITED(status)) {
599				ret = WEXITSTATUS(status);
600			} else {
601				UNRESOLVED(td->status,
602					   "Child process was neither killed nor exited");
603			}
604
605			if (ret != 0) {
606				exit(ret);	/* Output has already been closed in child */
607			}
608		} else {	/* child was a thread */
609
610			ret = pthread_join(child_th, NULL);
611			if (ret != 0) {
612				UNRESOLVED(ret,
613					   "[parent] Unable to join the thread");
614			}
615		}
616
617/**********
618 * Destroy the data
619 */
620		ret = pthread_cond_destroy(&(td->cnd));
621		if (ret != 0) {
622			UNRESOLVED(ret, "Failed to destroy the cond var");
623		}
624
625		ret = pthread_mutex_destroy(&(td->mtx));
626		if (ret != 0) {
627			UNRESOLVED(ret, "Failed to destroy the mutex");
628		}
629
630		ret = pthread_condattr_destroy(&ca);
631		if (ret != 0) {
632			UNRESOLVED(ret,
633				   "Failed to destroy the cond var attribute object");
634		}
635
636		ret = pthread_mutexattr_destroy(&ma);
637		if (ret != 0) {
638			UNRESOLVED(ret,
639				   "Failed to destroy the mutex attribute object");
640		}
641
642	}			/* Proceed to the next scenario */
643
644#if VERBOSE > 0
645	output("Test passed\n");
646#endif
647
648	PASSED;
649}
650
651#else /* WITHOUT_XOPEN */
652int main(void)
653{
654	output_init();
655	UNTESTED("This test requires XSI features");
656}
657#endif
658