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