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