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 file is a stress test for the function pthread_cond_timedwait.
18 *
19 *It aims to check the following assertion:
20 * When inside the function, the thread releases the mutex
21 * before waiting for the conditionnal variable.
22 * Those two operations are atomic in the mean that
23 * no other thread can gain access to the mutex
24 * then signal (or broadcast) the condition
25 * without the blocked thread behaving as if
26 * this signal (or broadcast) had happened
27 * after it blocked on the conditionnal variable.
28
29 * The steps are:
30 * -> Create N mutex & N cond vars with different attributes
31 * -> Create N threads A, which
32 * -> locks the mutex
33 * -> create a thread B, which
34 * -> locks the mutex
35 * -> while the boolean is false,
36 * -> broadcasts the condvar
37 * -> timedwaits the condition for 10 seconds
38 * -> broadcasts the condvar
39 * -> unlock the mutex
40 * -> while the boolean is false,
41 * -> timedwaits the condvar for 10 seconds
42 * -> signals the condvar
43 * -> unlock the mutex
44 * -> joins the thread B
45 * -> sets the boolean True when it receives SIGUSR1
46 * -> joins the N threads A.
47 *
48 * the test fails when a broadcast returns with a timeout.
49 *
50 * To test for pshared primitive, thread B could be in another process.
51 */
52
53 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
54 #define _POSIX_C_SOURCE 200112L
55
56 /* We need the XSI extention for the mutex attributes
57 and the mkstemp() routine */
58 #ifndef WITHOUT_XOPEN
59 #define _XOPEN_SOURCE 600
60 #endif
61 /********************************************************************************************/
62 /****************************** standard includes *****************************************/
63 /********************************************************************************************/
64 #include <pthread.h>
65 #include <stdarg.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <unistd.h>
69
70 #include <errno.h>
71 #include <signal.h>
72 #include <sys/wait.h>
73 #include <sys/mman.h>
74 #include <string.h>
75 #include <time.h>
76
77 /********************************************************************************************/
78 /****************************** Test framework *****************************************/
79 /********************************************************************************************/
80 #include "testfrmw.h"
81 #include "testfrmw.c"
82 /* This header is responsible for defining the following macros:
83 * UNRESOLVED(ret, descr);
84 * where descr is a description of the error and ret is an int (error code for example)
85 * FAILED(descr);
86 * where descr is a short text saying why the test has failed.
87 * PASSED();
88 * No parameter.
89 *
90 * Both three macros shall terminate the calling process.
91 * The testcase shall not terminate in any other maneer.
92 *
93 * The other file defines the functions
94 * void output_init()
95 * void output(char * string, ...)
96 *
97 * Those may be used to output information.
98 */
99
100 /********************************************************************************************/
101 /********************************** Configuration ******************************************/
102 /********************************************************************************************/
103 #ifndef SCALABILITY_FACTOR
104 #define SCALABILITY_FACTOR 1
105 #endif
106 #ifndef VERBOSE
107 #define VERBOSE 1
108 #endif
109
110 /* Number of children for each test scenario */
111 #define NCHILDREN (5)
112
113 #define TIMEOUT 120
114
115 #ifndef WITHOUT_ALTCLK
116 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */
117 #endif
118
119 /********************************************************************************************/
120 /*********************************** Test case *****************************************/
121 /********************************************************************************************/
122
123 #ifdef WITHOUT_XOPEN
124 /* We define those to avoid compilation errors, but they won't be used */
125 #define PTHREAD_MUTEX_DEFAULT 0
126 #define PTHREAD_MUTEX_NORMAL 0
127 #define PTHREAD_MUTEX_ERRORCHECK 0
128 #define PTHREAD_MUTEX_RECURSIVE 0
129
130 #endif
131
132 struct _scenar {
133 int m_type; /* Mutex type to use */
134 int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */
135 int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */
136 int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */
137 char *descr; /* Case description */
138 } scenarii[] = {
139 {
140 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}
141 , {
142 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}
143 , {
144 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}
145 , {
146 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}
147
148 , {
149 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}
150 , {
151 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}
152 , {
153 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}
154 , {
155 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}
156
157 , {
158 PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
159 "Pshared default mutex across processes"}
160 , {
161 PTHREAD_MUTEX_NORMAL, 1, 0, 1,
162 "Pshared normal mutex across processes"}
163 , {
164 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
165 "Pshared errorcheck mutex across processes"}
166 , {
167 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
168 "Pshared recursive mutex across processes"}
169
170 #ifdef USE_ALTCLK
171 , {
172 PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
173 "Pshared default mutex and alt clock condvar across processes"}
174 , {
175 PTHREAD_MUTEX_NORMAL, 1, 1, 1,
176 "Pshared normal mutex and alt clock condvar across processes"}
177 , {
178 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
179 "Pshared errorcheck mutex and alt clock condvar across processes"}
180 , {
181 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
182 "Pshared recursive mutex and alt clock condvar across processes"}
183
184 , {
185 PTHREAD_MUTEX_DEFAULT, 0, 1, 0,
186 "Default mutex and alt clock condvar"}
187 , {
188 PTHREAD_MUTEX_NORMAL, 0, 1, 0,
189 "Normal mutex and alt clock condvar"}
190 , {
191 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
192 "Errorcheck mutex and alt clock condvar"}
193 , {
194 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
195 "Recursive mutex and alt clock condvar"}
196
197 , {
198 PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
199 "PShared default mutex and alt clock condvar"}
200 , {
201 PTHREAD_MUTEX_NORMAL, 1, 1, 0,
202 "Pshared normal mutex and alt clock condvar"}
203 , {
204 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
205 "Pshared errorcheck mutex and alt clock condvar"}
206 , {
207 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
208 "Pshared recursive mutex and alt clock condvar"}
209 #endif
210 };
211
212 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
213
214 #define NTOT (NSCENAR * SCALABILITY_FACTOR * NCHILDREN)
215
216 struct childdata {
217 pthread_mutex_t mtx;
218 pthread_cond_t cnd;
219 clockid_t cid;
220 int fork;
221 int *pBool;
222 };
223
224 typedef struct {
225 struct childdata cd[NTOT];
226 int boolean;
227 } testdata_t;
228
229 pthread_attr_t ta;
230
231 /***
232 * The grand child function (either sub-thread or sub-process)
233 */
threaded_B(void * arg)234 void *threaded_B(void *arg)
235 {
236 int ret;
237 struct timespec ts;
238 struct childdata *cd = (struct childdata *)arg;
239
240 ret = pthread_mutex_lock(&(cd->mtx));
241 if (ret != 0) {
242 UNRESOLVED(ret, "[gchild] Unable to lock mutex");
243 }
244
245 while (*(cd->pBool) == 0) {
246 ret = pthread_cond_broadcast(&(cd->cnd));
247 if (ret != 0) {
248 UNRESOLVED(ret, "[gchild] Broadcast failed");
249 }
250
251 ret = clock_gettime(cd->cid, &ts);
252 if (ret != 0) {
253 UNRESOLVED(errno, "[gchild] Unable to read clock");
254 }
255
256 ts.tv_sec += TIMEOUT;
257
258 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts);
259 if (ret == ETIMEDOUT) {
260 FAILED
261 ("[gchild] Timeout occured. This means a cond signal was lost -- or parent died");
262 }
263 if (ret != 0) {
264 UNRESOLVED(ret, "[gchild] Failed to wait the cond");
265 }
266 }
267
268 /* We shall broadcast again to be sure the parent is not hung */
269 ret = pthread_cond_broadcast(&(cd->cnd));
270 if (ret != 0) {
271 UNRESOLVED(ret, "[gchild] Broadcast failed");
272 }
273
274 ret = pthread_mutex_unlock(&(cd->mtx));
275 if (ret != 0) {
276 UNRESOLVED(ret, "[gchild] Failed to finally release the mutex");
277 }
278
279 return NULL;
280 }
281
282 /***
283 * The child function (always in the main thread)
284 */
threaded_A(void * arg)285 void *threaded_A(void *arg)
286 {
287 struct childdata *cd = (struct childdata *)arg;
288 int ret, status;
289 pid_t child_p = 0, wrc;
290 pthread_t child_t;
291
292 struct timespec ts;
293
294 ret = pthread_mutex_lock(&(cd->mtx));
295 if (ret != 0) {
296 UNRESOLVED(ret, "[child] Unable to lock mutex");
297 }
298
299 /* Create the grand child */
300 if (cd->fork == 0) {
301 ret = pthread_create(&child_t, &ta, threaded_B, arg);
302 if (ret != 0) {
303 UNRESOLVED(ret,
304 "[child] Failed to create a grand child thread");
305 }
306 } else {
307 child_p = fork();
308 if (child_p == -1) {
309 UNRESOLVED(ret,
310 "[child] Failed to create a grand child proces");
311 }
312
313 if (child_p == 0) { /* grand child */
314 threaded_B(arg);
315 exit(0);
316 }
317 }
318
319 while (*(cd->pBool) == 0) {
320 ret = clock_gettime(cd->cid, &ts);
321 if (ret != 0) {
322 UNRESOLVED(errno, "[child] Unable to read clock");
323 }
324
325 ts.tv_sec += TIMEOUT;
326
327 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts);
328 if (ret == ETIMEDOUT) {
329 FAILED
330 ("[child] Timeout occured. This means a cond broadcast was lost -- or gchild died");
331 }
332 if (ret != 0) {
333 UNRESOLVED(ret, "[child] Failed to wait the cond");
334 }
335
336 ret = pthread_cond_signal(&(cd->cnd));
337 if (ret != 0) {
338 UNRESOLVED(ret, "[child] Signal failed");
339 }
340 }
341
342 ret = pthread_mutex_unlock(&(cd->mtx));
343 if (ret != 0) {
344 UNRESOLVED(ret, "[gchild] Failed to finally release the mutex");
345 }
346
347 /* Wait for the grand child termination */
348 if (cd->fork == 0) {
349 ret = pthread_join(child_t, NULL);
350 if (ret != 0) {
351 UNRESOLVED(ret,
352 "[child] Failed to join a grand child thread");
353 }
354 } else {
355 wrc = waitpid(child_p, &status, 0);
356 if (wrc != child_p) {
357 output("Expected pid: %i. Got %i\n", (int)child_p,
358 (int)wrc);
359 UNRESOLVED(errno, "Waitpid failed");
360 }
361
362 if (WIFSIGNALED(status)) {
363 output("Child process killed with signal %d\n",
364 WTERMSIG(status));
365 UNRESOLVED(0, "Child process was killed");
366 }
367
368 if (WIFEXITED(status)) {
369 ret = WEXITSTATUS(status);
370 } else {
371 UNRESOLVED(0,
372 "Child process was neither killed nor exited");
373 }
374 }
375
376 /* the end */
377 return NULL;
378 }
379
380 int *pBoolean = NULL;
381
382 /***
383 * Signal handler
384 */
sighdl(int sig)385 void sighdl(int sig)
386 {
387 #if VERBOSE > 1
388 output("Received the USR1 signal; stopping everything\n");
389 #endif
390 *pBoolean = 1;
391 }
392
main(int argc,char * argv[])393 int main(int argc, char *argv[])
394 {
395 int ret, i, j;
396 struct sigaction sa;
397
398 pthread_mutexattr_t ma;
399 pthread_condattr_t ca;
400 clockid_t cid = CLOCK_REALTIME;
401
402 testdata_t *td;
403 testdata_t alternativ;
404
405 int do_fork;
406 long pshared, monotonic, cs, mf;
407
408 pthread_t th[NTOT];
409
410 output_init();
411
412 pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
413 cs = sysconf(_SC_CLOCK_SELECTION);
414 monotonic = sysconf(_SC_MONOTONIC_CLOCK);
415 mf = sysconf(_SC_MAPPED_FILES);
416
417 #if VERBOSE > 0
418 output("Test starting\n");
419 output("System abilities:\n");
420 output(" TPS : %li\n", pshared);
421 output(" CS : %li\n", cs);
422 output(" MON : %li\n", monotonic);
423 output(" MF : %li\n", mf);
424 if ((mf < 0) || (pshared < 0))
425 output("Process-shared attributes won't be tested\n");
426 if ((cs < 0) || (monotonic < 0))
427 output("Alternative clock won't be tested\n");
428 #endif
429
430 /* We are not interested in testing the clock if we have no other clock available.. */
431 if (monotonic < 0)
432 cs = -1;
433
434 #ifndef USE_ALTCLK
435 if (cs > 0)
436 output
437 ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
438 #endif
439
440 /**********
441 * Allocate space for the testdata structure
442 */
443 if (mf < 0) {
444 /* Cannot mmap a file, we use an alternative method */
445 td = &alternativ;
446 pshared = -1; /* We won't do this testing anyway */
447 #if VERBOSE > 0
448 output("Testdata allocated in the process memory.\n");
449 #endif
450 } else {
451 /* We will place the test data in a mmaped file */
452 char filename[] = "/tmp/cond_timedwait_st1-XXXXXX";
453 size_t sz, ps;
454 void *mmaped;
455 int fd;
456 char *tmp;
457
458 /* We now create the temp files */
459 fd = mkstemp(filename);
460 if (fd == -1) {
461 UNRESOLVED(errno,
462 "Temporary file could not be created");
463 }
464
465 /* and make sure the file will be deleted when closed */
466 unlink(filename);
467
468 #if VERBOSE > 1
469 output("Temp file created (%s).\n", filename);
470 #endif
471
472 ps = (size_t) sysconf(_SC_PAGESIZE);
473 sz = ((sizeof(testdata_t) / ps) + 1) * ps; /* # pages needed to store the testdata */
474
475 tmp = calloc(1, sz);
476 if (tmp == NULL) {
477 UNRESOLVED(errno, "Memory allocation failed");
478 }
479
480 /* Write the data to the file. */
481 if (write(fd, tmp, sz) != (ssize_t) sz) {
482 UNRESOLVED(sz, "Writting to the file failed");
483 }
484
485 free(tmp);
486
487 /* Now we can map the file in memory */
488 mmaped =
489 mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
490 if (mmaped == MAP_FAILED) {
491 UNRESOLVED(errno, "mmap failed");
492 }
493
494 td = (testdata_t *) mmaped;
495
496 /* Our datatest structure is now in shared memory */
497 #if VERBOSE > 1
498 output("Testdata allocated in shared memory (%ib).\n",
499 sizeof(testdata_t));
500 #endif
501 }
502
503 /* Init the signal handler variable */
504 pBoolean = &(td->boolean);
505
506 /* Init the structure */
507 for (i = 0; i < NSCENAR; i++) {
508 #if VERBOSE > 1
509 output("[parent] Preparing attributes for: %s\n",
510 scenarii[i].descr);
511 #ifdef WITHOUT_XOPEN
512 output("[parent] Mutex attributes DISABLED -> not used\n");
513 #endif
514 #endif
515 /* set / reset everything */
516 do_fork = 0;
517 ret = pthread_mutexattr_init(&ma);
518 if (ret != 0) {
519 UNRESOLVED(ret,
520 "[parent] Unable to initialize the mutex attribute object");
521 }
522 ret = pthread_condattr_init(&ca);
523 if (ret != 0) {
524 UNRESOLVED(ret,
525 "[parent] Unable to initialize the cond attribute object");
526 }
527 #ifndef WITHOUT_XOPEN
528 /* Set the mutex type */
529 ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type);
530 if (ret != 0) {
531 UNRESOLVED(ret, "[parent] Unable to set mutex type");
532 }
533 #if VERBOSE > 1
534 output("[parent] Mutex type : %i\n", scenarii[i].m_type);
535 #endif
536 #endif
537
538 /* Set the pshared attributes, if supported */
539 if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) {
540 ret =
541 pthread_mutexattr_setpshared(&ma,
542 PTHREAD_PROCESS_SHARED);
543 if (ret != 0) {
544 UNRESOLVED(ret,
545 "[parent] Unable to set the mutex process-shared");
546 }
547 ret =
548 pthread_condattr_setpshared(&ca,
549 PTHREAD_PROCESS_SHARED);
550 if (ret != 0) {
551 UNRESOLVED(ret,
552 "[parent] Unable to set the cond var process-shared");
553 }
554 #if VERBOSE > 1
555 output("[parent] Mutex & cond are process-shared\n");
556 #endif
557 }
558 #if VERBOSE > 1
559 else {
560 output("[parent] Mutex & cond are process-private\n");
561 }
562 #endif
563
564 /* Set the alternative clock, if supported */
565 #ifdef USE_ALTCLK
566 if ((cs > 0) && (scenarii[i].c_clock != 0)) {
567 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
568 if (ret != 0) {
569 UNRESOLVED(ret,
570 "[parent] Unable to set the monotonic clock for the cond");
571 }
572 #if VERBOSE > 1
573 output("[parent] Cond uses the Monotonic clock\n");
574 #endif
575 }
576 #if VERBOSE > 1
577 else {
578 output("[parent] Cond uses the default clock\n");
579 }
580 #endif
581 ret = pthread_condattr_getclock(&ca, &cid);
582 if (ret != 0) {
583 UNRESOLVED(ret, "Unable to get clock from cond attr");
584 }
585 #endif
586
587 /* Tell whether the test will be across processes */
588 if ((pshared > 0) && (scenarii[i].fork != 0)) {
589 do_fork = 1;
590 #if VERBOSE > 1
591 output("[parent] Child will be a new process\n");
592 #endif
593 }
594 #if VERBOSE > 1
595 else {
596 output("[parent] Child will be a new thread\n");
597 }
598 #endif
599
600 /* Initialize all the mutex and condvars which uses those attributes */
601 for (j = 0; j < SCALABILITY_FACTOR * NCHILDREN; j++) {
602 #define CD (td->cd[i+(j*NSCENAR)])
603 CD.pBool = &(td->boolean);
604 CD.fork = do_fork;
605 CD.cid = cid;
606
607 /* initialize the condvar */
608 ret = pthread_cond_init(&(CD.cnd), &ca);
609 if (ret != 0) {
610 UNRESOLVED(ret, "[parent] Cond init failed");
611 }
612
613 /* initialize the mutex */
614 ret = pthread_mutex_init(&(CD.mtx), &ma);
615 if (ret != 0) {
616 UNRESOLVED(ret, "[parent] Mutex init failed");
617 }
618 #undef CD
619 }
620
621 ret = pthread_condattr_destroy(&ca);
622 if (ret != 0) {
623 UNRESOLVED(ret,
624 "Failed to destroy the cond var attribute object");
625 }
626
627 ret = pthread_mutexattr_destroy(&ma);
628 if (ret != 0) {
629 UNRESOLVED(ret,
630 "Failed to destroy the mutex attribute object");
631 }
632 }
633 #if VERBOSE > 1
634 output("[parent] All condvars & mutex are ready\n");
635 #endif
636
637 ret = pthread_attr_init(&ta);
638 if (ret != 0) {
639 UNRESOLVED(ret,
640 "[parent] Failed to initialize a thread attribute object");
641 }
642 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
643 if (ret != 0) {
644 UNRESOLVED(ret, "[parent] Failed to set thread stack size");
645 }
646
647 sigemptyset(&sa.sa_mask);
648 sa.sa_flags = 0;
649 sa.sa_handler = sighdl;
650 if ((ret = sigaction(SIGUSR1, &sa, NULL))) {
651 UNRESOLVED(ret, "Unable to register signal handler");
652 }
653 #if VERBOSE > 1
654 output("[parent] Signal handler registered\n");
655 #endif
656
657 for (i = 0; i < NTOT; i++) {
658 ret = pthread_create(&th[i], &ta, threaded_A, &(td->cd[i]));
659 /* In case of failure we can exit; the child process will die after a while */
660 if (ret != 0) {
661 UNRESOLVED(ret, "[Parent] Failed to create a thread");
662 }
663 #if VERBOSE > 1
664 if ((i % 10) == 0)
665 output("[parent] %i threads created...\n", i + 1);
666 #endif
667 }
668
669 #if VERBOSE > 1
670 output("[parent] All %i threads are running...\n", NTOT);
671 #endif
672
673 for (i = 0; i < NTOT; i++) {
674 ret = pthread_join(th[i], NULL);
675 if (ret != 0) {
676 UNRESOLVED(ret, "[Parent] Failed to join a thread");
677 }
678 }
679
680 /* Destroy everything */
681 for (i = 0; i < NTOT; i++) {
682 /* destroy the condvar */
683 ret = pthread_cond_destroy(&(td->cd[i].cnd));
684 if (ret != 0) {
685 UNRESOLVED(ret, "[parent] Cond destroy failed");
686 }
687
688 /* destroy the mutex */
689 ret = pthread_mutex_init(&(td->cd[i].mtx), &ma);
690 if (ret != 0) {
691 UNRESOLVED(ret, "[parent] Mutex destroy failed");
692 }
693 }
694
695 #if VERBOSE > 0
696 output("Test passed\n");
697 #endif
698
699 PASSED;
700 }
701