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 abstime parameter is invalid,
20 * the function must return EINVAL and
21 * the mutex state must not have changed during the call.
22
23 * The steps are:
24 *  -> parent (for each mutex type and each condvar options, across threads or processes)
25 *     -> locks the mutex m
26 *     -> sets ctrl = 0
27 *     -> creates a bunch of children, which:
28 *        -> lock the mutex m
29 *        -> if ctrl == 0, test has failed
30 *        -> unlock the mutex then exit
31 *     -> calls pthread_cond_timedwait with invalid values (nsec > 999999999)
32 *     -> sets ctrl = non-zero value
33 *     -> unlocks the mutex m
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#include <pthread.h>
46#include <stdarg.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <unistd.h>
50
51#include <errno.h>
52#include <sys/wait.h>
53#include <sys/mman.h>
54#include <string.h>
55#include <time.h>
56
57#include "../testfrmw/testfrmw.h"
58#include "../testfrmw/testfrmw.c"
59
60#ifndef VERBOSE
61#define VERBOSE 1
62#endif
63
64#define NCHILDREN (20)
65
66#ifndef WITHOUT_ALTCLK
67#define USE_ALTCLK		/* make tests with MONOTONIC CLOCK if supported */
68#endif
69
70#ifndef WITHOUT_XOPEN
71
72typedef struct {
73	pthread_mutex_t mtx;
74	int ctrl;		/* Control value */
75	int gotit;		/* Thread locked the mutex while ctrl == 0 */
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
159struct {
160	long sec_val;		/* Value for seconds */
161	short sec_is_offset;	/* Seconds value is added to current time or is absolute */
162	long nsec_val;		/* Value for nanoseconds */
163	short nsec_is_offset;	/* Nanoseconds value is added to current time or is absolute */
164} junks_ts[] = {
165	{
166	-2, 1, 1000000000, 1}
167	, {
168	-2, 1, -1, 0}
169	, {
170	-3, 1, 2000000000, 0}
171};
172
173void *tf(void *arg)
174{
175	int ret = 0;
176
177	testdata_t *td = (testdata_t *) arg;
178
179	/* Lock the mutex */
180	ret = pthread_mutex_lock(&(td->mtx));
181	if (ret != 0) {
182		td->status = ret;
183		UNRESOLVED(ret, "[child] Unable to lock the mutex");
184	}
185
186	/* Checks whether the parent release the lock inside the timedwait function */
187	if (td->ctrl == 0)
188		td->gotit += 1;
189
190	/* Unlock and exit */
191	ret = pthread_mutex_unlock(&(td->mtx));
192	if (ret != 0) {
193		td->status = ret;
194		UNRESOLVED(ret, "[child] Failed to unlock the mutex.");
195	}
196	return NULL;
197}
198
199int main(void)
200{
201	int ret, k;
202	unsigned int i, j;
203	pthread_mutexattr_t ma;
204	pthread_condattr_t ca;
205	pthread_cond_t cnd;
206	clockid_t cid = CLOCK_REALTIME;
207	struct timespec ts, ts_junk;
208
209	testdata_t *td;
210	testdata_t alternativ;
211
212	int do_fork;
213
214	pid_t child_pr[NCHILDREN], chkpid;
215	int status;
216	pthread_t child_th[NCHILDREN];
217
218	long pshared, monotonic, cs, mf;
219
220	output_init();
221	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
222	cs = sysconf(_SC_CLOCK_SELECTION);
223	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
224	mf = sysconf(_SC_MAPPED_FILES);
225
226#if VERBOSE > 0
227	output("Test starting\n");
228	output("System abilities:\n");
229	output(" TPS : %li\n", pshared);
230	output(" CS  : %li\n", cs);
231	output(" MON : %li\n", monotonic);
232	output(" MF  : %li\n", mf);
233	if ((mf < 0) || (pshared < 0))
234		output("Process-shared attributes won't be tested\n");
235	if ((cs < 0) || (monotonic < 0))
236		output("Alternative clock won't be tested\n");
237	fflush(stdout);
238#endif
239
240	/* We are not interested in testing the clock if we have no other clock available.. */
241	if (monotonic < 0)
242		cs = -1;
243
244#ifndef USE_ALTCLK
245	if (cs > 0)
246		output
247		    ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
248#endif
249
250/**********
251 * Allocate space for the testdata structure
252 */
253	if (mf < 0) {
254		/* Cannot mmap a file, we use an alternative method */
255		td = &alternativ;
256		pshared = -1;	/* We won't do this testing anyway */
257#if VERBOSE > 0
258		output("Testdata allocated in the process memory.\n");
259#endif
260	} else {
261		/* We will place the test data in a mmaped file */
262		char filename[] = "/tmp/cond_timedwait_2-4-XXXXXX";
263		size_t sz;
264		void *mmaped;
265		int fd;
266		char *tmp;
267
268		/* We now create the temp files */
269		fd = mkstemp(filename);
270		if (fd == -1) {
271			UNRESOLVED(errno,
272				   "Temporary file could not be created");
273		}
274
275		/* and make sure the file will be deleted when closed */
276		unlink(filename);
277
278#if VERBOSE > 1
279		output("Temp file created (%s).\n", filename);
280#endif
281
282		sz = (size_t) sysconf(_SC_PAGESIZE);
283
284		tmp = calloc(1, sz);
285		if (tmp == NULL) {
286			UNRESOLVED(errno, "Memory allocation failed");
287		}
288
289		/* Write the data to the file.  */
290		if (write(fd, tmp, sz) != (ssize_t) sz) {
291			UNRESOLVED(sz, "Writting to the file failed");
292		}
293
294		free(tmp);
295
296		/* Now we can map the file in memory */
297		mmaped =
298		    mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
299		if (mmaped == MAP_FAILED) {
300			UNRESOLVED(errno, "mmap failed");
301		}
302
303		td = (testdata_t *) mmaped;
304
305		/* Our datatest structure is now in shared memory */
306#if VERBOSE > 1
307		output("Testdata allocated in shared memory.\n");
308#endif
309	}
310
311/**********
312 * For each test scenario, initialize the attributes and other variables.
313 * Do the whole thing for each time to test.
314 */
315	for (i = 0; i < (sizeof(scenarii) / sizeof(scenarii[0])); i++) {
316		for (j = 0; j < (sizeof(junks_ts) / sizeof(junks_ts[0])); j++) {
317#if VERBOSE > 1
318			output("[parent] Preparing attributes for: %s\n",
319			       scenarii[i].descr);
320#endif
321			/* set / reset everything */
322			do_fork = 0;
323			ret = pthread_mutexattr_init(&ma);
324			if (ret != 0) {
325				UNRESOLVED(ret,
326					   "[parent] Unable to initialize the mutex attribute object");
327			}
328			ret = pthread_condattr_init(&ca);
329			if (ret != 0) {
330				UNRESOLVED(ret,
331					   "[parent] Unable to initialize the cond attribute object");
332			}
333
334			/* Set the mutex type */
335			ret =
336			    pthread_mutexattr_settype(&ma, scenarii[i].m_type);
337			if (ret != 0) {
338				UNRESOLVED(ret,
339					   "[parent] Unable to set mutex type");
340			}
341#if VERBOSE > 1
342			output("[parent] Mutex type : %i\n",
343			       scenarii[i].m_type);
344#endif
345
346			/* Set the pshared attributes, if supported */
347			if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) {
348				ret =
349				    pthread_mutexattr_setpshared(&ma,
350								 PTHREAD_PROCESS_SHARED);
351				if (ret != 0) {
352					UNRESOLVED(ret,
353						   "[parent] Unable to set the mutex process-shared");
354				}
355				ret =
356				    pthread_condattr_setpshared(&ca,
357								PTHREAD_PROCESS_SHARED);
358				if (ret != 0) {
359					UNRESOLVED(ret,
360						   "[parent] Unable to set the cond var process-shared");
361				}
362#if VERBOSE > 1
363				output
364				    ("[parent] Mutex & cond are process-shared\n");
365#endif
366			}
367#if VERBOSE > 1
368			else {
369				output
370				    ("[parent] Mutex & cond are process-private\n");
371			}
372#endif
373
374			/* Set the alternative clock, if supported */
375#ifdef USE_ALTCLK
376			if ((cs > 0) && (scenarii[i].c_clock != 0)) {
377				ret =
378				    pthread_condattr_setclock(&ca,
379							      CLOCK_MONOTONIC);
380				if (ret != 0) {
381					UNRESOLVED(ret,
382						   "[parent] Unable to set the monotonic clock for the cond");
383				}
384#if VERBOSE > 1
385				output
386				    ("[parent] Cond uses the Monotonic clock\n");
387#endif
388			}
389#if VERBOSE > 1
390			else {
391				output
392				    ("[parent] Cond uses the default clock\n");
393			}
394#endif
395			ret = pthread_condattr_getclock(&ca, &cid);
396			if (ret != 0) {
397				UNRESOLVED(ret,
398					   "Unable to get clock from cond attr");
399			}
400#endif
401
402			/* Tell whether the test will be across processes */
403			if ((pshared > 0) && (scenarii[i].fork != 0)) {
404				do_fork = 1;
405#if VERBOSE > 1
406				output
407				    ("[parent] Child will be a new process\n");
408#endif
409			}
410#if VERBOSE > 1
411			else {
412				output("[parent] Child will be a new thread\n");
413			}
414#endif
415
416			/* initialize the condvar */
417			ret = pthread_cond_init(&cnd, &ca);
418			if (ret != 0) {
419				UNRESOLVED(ret, "[parent] Cond init failed");
420			}
421
422/**********
423 * Initialize the testdata_t structure with the previously defined attributes
424 */
425			/* Initialize the mutex */
426			ret = pthread_mutex_init(&(td->mtx), &ma);
427			if (ret != 0) {
428				UNRESOLVED(ret, "[parent] Mutex init failed");
429			}
430
431			/* Initialize the other datas from the test structure */
432			td->ctrl = 0;
433			td->gotit = 0;
434			td->status = 0;
435
436/**********
437 * Proceed to the actual testing
438 */
439			/* Lock the mutex before creating children */
440			ret = pthread_mutex_lock(&(td->mtx));
441			if (ret != 0) {
442				UNRESOLVED(ret,
443					   "[parent] Unable to lock the mutex");
444			}
445
446			/* Create the children */
447			if (do_fork != 0) {
448				/* We are testing across processes */
449				for (k = 0; k < NCHILDREN; k++) {
450					child_pr[k] = fork();
451					if (child_pr[k] == -1) {
452						UNRESOLVED(errno,
453							   "[parent] Fork failed");
454					}
455
456					if (child_pr[k] == 0) {
457#if VERBOSE > 3
458						output
459						    ("[child] Child process %i starting...\n",
460						     k);
461#endif
462
463						if (tf((void *)td) != NULL) {
464							UNRESOLVED(-1,
465								   "[child] Got an unexpected return value from test function");
466						} else {
467							/* We cannot use the PASSED macro here since it would terminate the output */
468							exit(0);
469						}
470					}
471				}
472				/* Only the parent process goes further */
473			} else {	/* do_fork == 0 */
474
475				/* We are testing across two threads */
476				for (k = 0; k < NCHILDREN; k++) {
477					ret =
478					    pthread_create(&child_th[k], NULL,
479							   tf, td);
480					if (ret != 0) {
481						UNRESOLVED(ret,
482							   "[parent] Unable to create the child thread.");
483					}
484				}
485			}
486
487			/* Children are now running and trying to lock the mutex. */
488
489			ret = clock_gettime(cid, &ts);
490			if (ret != 0) {
491				UNRESOLVED(ret,
492					   "[parent] Unable to read clock");
493			}
494
495			/* Do the junk timedwaits */
496			ts_junk.tv_sec =
497			    junks_ts[j].sec_val +
498			    (junks_ts[j].sec_is_offset ? ts.tv_sec : 0);
499			ts_junk.tv_nsec =
500			    junks_ts[j].nsec_val +
501			    (junks_ts[j].nsec_is_offset ? ts.tv_nsec : 0);
502
503#if VERBOSE > 2
504			output("TS: s = %s%li ; ns = %s%li\n",
505			       junks_ts[j].sec_is_offset ? "n + " : " ",
506			       junks_ts[j].sec_val,
507			       junks_ts[j].nsec_is_offset ? "n + " : " ",
508			       junks_ts[j].nsec_val);
509			output("Now is: %i.%09li\n", ts.tv_sec, ts.tv_nsec);
510			output("Junk is: %i.%09li\n", ts_junk.tv_sec,
511			       ts_junk.tv_nsec);
512#endif
513
514			do {
515				ret =
516				    pthread_cond_timedwait(&cnd, &(td->mtx),
517							   &ts_junk);
518			} while (ret == 0);
519#if VERBOSE > 2
520			output("timedwait returns %d (%s) - gotit = %d\n", ret,
521			       strerror(ret), td->gotit);
522#endif
523
524			/* check that when EINVAL is returned, the mutex has not been released */
525			if (ret == EINVAL) {
526				if (td->gotit != 0) {
527					FAILED
528					    ("The mutex was released when an invalid timestamp was detected in the function");
529				}
530#if VERBOSE > 0
531			} else {
532				output
533				    ("Warning, struct timespec with tv_sec = %i and tv_nsec = %li was not invalid\n",
534				     ts_junk.tv_sec, ts_junk.tv_nsec);
535			}
536#endif
537
538			/* Finally unlock the mutex */
539			td->ctrl = 1;
540			ret = pthread_mutex_unlock(&(td->mtx));
541			if (ret != 0) {
542				UNRESOLVED(ret,
543					   "[parent] Unable to unlock the mutex");
544			}
545
546			/* Wait for the child to terminate */
547			if (do_fork != 0) {
548				/* We were testing across processes */
549				ret = 0;
550				for (k = 0; k < NCHILDREN; k++) {
551					chkpid =
552					    waitpid(child_pr[k], &status, 0);
553					if (chkpid != child_pr[k]) {
554						output
555						    ("Expected pid: %i. Got %i\n",
556						     (int)child_pr[k],
557						     (int)chkpid);
558						UNRESOLVED(errno,
559							   "Waitpid failed");
560					}
561					if (WIFSIGNALED(status)) {
562						output
563						    ("Child process killed with signal %d\n",
564						     WTERMSIG(status));
565						UNRESOLVED(-1,
566							   "Child process was killed");
567					}
568
569					if (WIFEXITED(status)) {
570						ret |= WEXITSTATUS(status);
571					} else {
572						UNRESOLVED(-1,
573							   "Child process was neither killed nor exited");
574					}
575				}
576				if (ret != 0) {
577					exit(ret);	/* Output has already been closed in child */
578				}
579
580			} else {	/* child was a thread */
581
582				for (k = 0; k < NCHILDREN; k++) {
583					ret = pthread_join(child_th[k], NULL);
584					if (ret != 0) {
585						UNRESOLVED(ret,
586							   "[parent] Unable to join the thread");
587					}
588				}
589			}
590
591/**********
592 * Destroy the data
593 */
594			ret = pthread_cond_destroy(&cnd);
595			if (ret != 0) {
596				UNRESOLVED(ret,
597					   "Failed to destroy the cond var");
598			}
599
600			ret = pthread_mutex_destroy(&(td->mtx));
601			if (ret != 0) {
602				UNRESOLVED(ret, "Failed to destroy the mutex");
603			}
604
605			ret = pthread_condattr_destroy(&ca);
606			if (ret != 0) {
607				UNRESOLVED(ret,
608					   "Failed to destroy the cond var attribute object");
609			}
610
611			ret = pthread_mutexattr_destroy(&ma);
612			if (ret != 0) {
613				UNRESOLVED(ret,
614					   "Failed to destroy the mutex attribute object");
615			}
616
617		}		/* Proceed to the next junk timedwait value */
618	}			/* Proceed to the next scenario */
619
620#if VERBOSE > 0
621	output("Test passed\n");
622#endif
623
624	PASSED;
625}
626
627#else /* WITHOUT_XOPEN */
628int main(void)
629{
630	output_init();
631	UNTESTED("This test requires XSI features");
632}
633#endif
634