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