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