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