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 tests the following assertion:
18 *
19 * The pthread_cond_broadcast function unblocks all the threads blocked on the
20 * conditional variable.
21
22 * The steps are:
23 * -> Create as many threads as possible which will wait on a condition
24 * variable
25 * -> broadcast the condition and check that all threads are awaken
26 *
27 * The test will fail when the threads are not terminated within a certain
28 * duration.
29 *
30 */
31
32 #include <pthread.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37
38 #include <errno.h>
39 #include <signal.h>
40 #include <string.h>
41 #include <time.h>
42 #include <sys/mman.h>
43 #include <sys/wait.h>
44 #include <semaphore.h>
45 #ifdef __linux__
46 #include <sys/sysinfo.h>
47 #endif
48
49 #include "../testfrmw/testfrmw.h"
50 #include "../testfrmw/testfrmw.c"
51
52 #define UNRESOLVED_KILLALL(error, text) { \
53 if (td->fork) { \
54 children_t *tmp; \
55 while (children->next != NULL) { \
56 tmp = children->next; \
57 children->next = tmp->next; \
58 kill(tmp->data.p, SIGKILL); \
59 free(tmp); \
60 } \
61 } \
62 UNRESOLVED(error, text); \
63 }
64 #define FAILED_KILLALL(text) { \
65 if (td->fork) { \
66 children_t *tmp; \
67 while (children->next != NULL) { \
68 tmp = children->next; \
69 children->next = tmp->next; \
70 kill(tmp->data.p, SIGKILL); \
71 free(tmp); \
72 } \
73 } \
74 FAILED(text); \
75 }
76
77 #ifndef VERBOSE
78 #define VERBOSE 1
79 #endif
80
81 /* Do not create more than this amount of children: */
82 static int max_process_children = 200;
83 static int max_thread_children = 1000;
84
85 #define TIMEOUT (180)
86
87 #ifndef WITHOUT_ALTCLK
88 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */
89 #endif
90
91 #ifdef WITHOUT_XOPEN
92 /* We define those to avoid compilation errors, but they won't be used */
93 #define PTHREAD_MUTEX_DEFAULT 0
94 #define PTHREAD_MUTEX_NORMAL 0
95 #define PTHREAD_MUTEX_ERRORCHECK 0
96 #define PTHREAD_MUTEX_RECURSIVE 0
97
98 #endif
99
100 static struct _scenar {
101 int m_type;
102 int mc_pshared;
103 int c_clock;
104 int fork;
105 char *descr;
106 } scenarii[] = {
107 {
108 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}, {
109 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}, {
110 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}, {
111 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}, {
112 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}, {
113 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}, {
114 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}, {
115 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}, {
116 PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
117 "Pshared default mutex across processes"}, {
118 PTHREAD_MUTEX_NORMAL, 1, 0, 1, "Pshared normal mutex across processes"},
119 {
120 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
121 "Pshared errorcheck mutex across processes"}, {
122 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
123 "Pshared recursive mutex across processes"},
124 #ifdef USE_ALTCLK
125 {
126 PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
127 "Pshared default mutex and alt clock condvar across processes"},
128 {
129 PTHREAD_MUTEX_NORMAL, 1, 1, 1,
130 "Pshared normal mutex and alt clock condvar across processes"},
131 {
132 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
133 "Pshared errorcheck mutex and alt clock condvar across processes"},
134 {
135 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
136 "Pshared recursive mutex and alt clock condvar across processes"},
137 {
138 PTHREAD_MUTEX_DEFAULT, 0, 1, 0, "Default mutex and alt clock condvar"},
139 {
140 PTHREAD_MUTEX_NORMAL, 0, 1, 0, "Normal mutex and alt clock condvar"},
141 {
142 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
143 "Errorcheck mutex and alt clock condvar"}, {
144 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
145 "Recursive mutex and alt clock condvar"}, {
146 PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
147 "PShared default mutex and alt clock condvar"}, {
148 PTHREAD_MUTEX_NORMAL, 1, 1, 0,
149 "Pshared normal mutex and alt clock condvar"}, {
150 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
151 "Pshared errorcheck mutex and alt clock condvar"}, {
152 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
153 "Pshared recursive mutex and alt clock condvar"},
154 #endif
155 };
156
157 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
158
159 struct testdata {
160 int count; /* number of children currently waiting */
161 pthread_cond_t cnd;
162 pthread_mutex_t mtx;
163 int predicate; /* Boolean associated to the condvar */
164 clockid_t cid; /* clock used in the condvar */
165 char fork; /* the children are processes */
166 };
167 static struct testdata *td;
168
169 /* Child function (either in a thread or in a process) */
child(void * arg PTS_ATTRIBUTE_UNUSED)170 static void *child(void *arg PTS_ATTRIBUTE_UNUSED)
171 {
172 int ret = 0;
173 struct timespec ts;
174 char timed;
175
176 /* lock the mutex */
177 ret = pthread_mutex_lock(&td->mtx);
178 if (ret != 0)
179 UNRESOLVED(ret, "Failed to lock mutex in child");
180
181 /* increment count */
182 td->count++;
183
184 timed = td->count & 1;
185
186 if (timed) {
187 /* get current time if we are a timedwait */
188 ret = clock_gettime(td->cid, &ts);
189 if (ret != 0)
190 UNRESOLVED(errno, "Unable to read clock");
191 ts.tv_sec += TIMEOUT;
192 }
193
194 do {
195 /* Wait while the predicate is false */
196 if (timed)
197 ret = pthread_cond_timedwait(&td->cnd, &td->mtx, &ts);
198 else
199 ret = pthread_cond_wait(&td->cnd, &td->mtx);
200 #if VERBOSE > 5
201 output("[child] Wokenup timed=%i, Predicate=%i, ret=%i\n",
202 timed, td->predicate, ret);
203 #endif
204 } while ((ret == 0) && (td->predicate == 0));
205 if (ret == ETIMEDOUT) {
206 ret = pthread_mutex_unlock(&td->mtx);
207 if (ret != 0)
208 UNRESOLVED(ret, "Failed to unlock the mutex.");
209 FAILED("Timeout occured. This means a cond signal was lost -- "
210 "or parent died");
211 }
212 if (ret != 0)
213 UNRESOLVED(ret, "Failed to wait for the cond");
214
215 /* unlock the mutex */
216 ret = pthread_mutex_unlock(&td->mtx);
217 if (ret != 0)
218 UNRESOLVED(ret, "Failed to unlock the mutex.");
219
220 return NULL;
221 }
222
223 /* Structure used to store the children information */
224 typedef struct _children {
225 union {
226 pid_t p;
227 pthread_t t;
228 } data;
229 struct _children *next;
230 } children_t;
231
232 static children_t sentinel = {.next = NULL };
233
234 static children_t *children = &sentinel;
235
236 /* Timeout thread */
237
238 static sem_t sem_tmr;
239
timer(void * arg PTS_ATTRIBUTE_UNUSED)240 static void *timer(void *arg PTS_ATTRIBUTE_UNUSED)
241 {
242 unsigned int to = TIMEOUT;
243 int ret;
244
245 do {
246 ret = sem_wait(&sem_tmr);
247 } while ((ret != 0) && (errno == EINTR));
248 if (ret != 0)
249 UNRESOLVED(errno, "Failed to wait for the semaphore "
250 "in timer thread.");
251
252 do {
253 to = sleep(to);
254 } while (to > 0);
255 FAILED_KILLALL("Operation timed out. A signal was lost.");
256 return NULL; /* For compiler */
257 }
258
259 #ifdef __linux__
children_number(void)260 static void children_number(void)
261 {
262 struct sysinfo sysinformation;
263 int ret;
264 int avail_number;
265 unsigned long per_process;
266 unsigned long min_stack;
267
268 min_stack = sysconf(_SC_THREAD_STACK_MIN);
269
270 ret = sysinfo(&sysinformation);
271 if (ret != 0)
272 UNRESOLVED(ret, "Failed to get system information.");
273
274 per_process = min_stack * max_thread_children;
275 if (per_process > sysinformation.freeram)
276 UNTESTED("Not enough memory.");
277
278 avail_number = sysinformation.freeram / per_process;
279
280 if (avail_number < 10)
281 UNTESTED("Not enough memory.");
282
283 max_process_children = (avail_number < max_process_children ?
284 avail_number : max_process_children);
285
286 return;
287 }
288 #else
children_number(void)289 static void children_number(void)
290 {
291 return;
292 }
293 #endif
294
main(void)295 int main(void)
296 {
297 int ret;
298
299 pthread_mutexattr_t ma;
300 pthread_condattr_t ca;
301
302 int scenar;
303 long pshared, monotonic, cs, mf;
304
305 int child_count;
306 children_t *tmp, *cur;
307
308 int ch;
309 pid_t pid;
310 int status;
311
312 pthread_t t_timer;
313
314 pthread_attr_t ta;
315
316 struct testdata alternativ;
317
318 output_init();
319
320 children_number();
321
322 /* check the system abilities */
323 pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
324 cs = sysconf(_SC_CLOCK_SELECTION);
325 monotonic = sysconf(_SC_MONOTONIC_CLOCK);
326 mf = sysconf(_SC_MAPPED_FILES);
327
328 #if VERBOSE > 0
329 output("Test starting\n");
330 output("System abilities:\n");
331 output(" TPS : %li\n", pshared);
332 output(" CS : %li\n", cs);
333 output(" MON : %li\n", monotonic);
334 output(" MF : %li\n", mf);
335 if ((mf < 0) || (pshared < 0))
336 output("Process-shared attributes won't be tested\n");
337 if ((cs < 0) || (monotonic < 0))
338 output("Alternative clock won't be tested\n");
339 fflush(stdout);
340 #endif
341
342 if (monotonic < 0)
343 cs = -1;
344
345 #ifndef USE_ALTCLK
346 if (cs > 0)
347 output("Implementation supports the MONOTONIC CLOCK "
348 "but option is disabled in test.\n");
349 #endif
350
351 /*
352 * Allocate space for the testdata structure
353 */
354 if (mf < 0) {
355 /* Cannot mmap a file, we use an alternative method */
356 td = &alternativ;
357 pshared = -1; /* We won't do this testing anyway */
358 #if VERBOSE > 0
359 output("Testdata allocated in the process memory.\n");
360 #endif
361 } else {
362 /* We will place the test data in a mmaped file */
363 char filename[] = "/tmp/cond_broadcast-XXXXXX";
364 size_t sz, ps;
365 void *mmaped;
366 int fd;
367 char *tmp;
368
369 /* We now create the temp files */
370 fd = mkstemp(filename);
371 if (fd == -1)
372 UNRESOLVED(errno,
373 "Temporary file could not be created");
374
375 /* and make sure the file will be deleted when closed */
376 unlink(filename);
377
378 #if VERBOSE > 1
379 output("Temp file created (%s).\n", filename);
380 #endif
381
382 ps = (size_t) sysconf(_SC_PAGESIZE);
383 sz = ((sizeof(struct testdata) / ps) + 1) * ps;
384
385 tmp = calloc(1, sz);
386 if (tmp == NULL)
387 UNRESOLVED(errno, "Memory allocation failed");
388
389 /* Write the data to the file. */
390 if (write(fd, tmp, sz) != (ssize_t) sz)
391 UNRESOLVED(sz, "Writting to the file failed");
392
393 free(tmp);
394
395 /* Now we can map the file in memory */
396 mmaped = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED,
397 fd, 0);
398 if (mmaped == MAP_FAILED)
399 UNRESOLVED(errno, "mmap failed");
400
401 td = (struct testdata *)mmaped;
402
403 /* Our datatest structure is now in shared memory */
404 #if VERBOSE > 1
405 output("Testdata allocated in shared memory (%ib).\n",
406 sizeof(struct testdata));
407 #endif
408 }
409
410 /* Initialize the thread attribute object */
411 ret = pthread_attr_init(&ta);
412 if (ret != 0)
413 UNRESOLVED(ret, "[parent] Failed to initialize a thread "
414 "attribute object");
415 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
416 if (ret != 0)
417 UNRESOLVED(ret, "[parent] Failed to set thread stack size");
418
419 /* Initialize the semaphore for the timer thread */
420 ret = sem_init(&sem_tmr, 0, 0);
421 if (ret != 0)
422 UNRESOLVED(ret, "Failed to initialize the semaphore");
423
424 /* Do the test for each test scenario */
425 for (scenar = 0; scenar < (int)NSCENAR; scenar++) {
426 /* set / reset everything */
427 td->fork = 0;
428 ret = pthread_mutexattr_init(&ma);
429 if (ret != 0)
430 UNRESOLVED(ret, "[parent] Unable to initialize the "
431 "mutex attribute object");
432 ret = pthread_condattr_init(&ca);
433 if (ret != 0)
434 UNRESOLVED(ret, "[parent] Unable to initialize the "
435 "cond attribute object");
436
437 #ifndef WITHOUT_XOPEN
438 /* Set the mutex type */
439 ret = pthread_mutexattr_settype(&ma, scenarii[scenar].m_type);
440 if (ret != 0)
441 UNRESOLVED(ret, "[parent] Unable to set mutex type");
442 #endif
443
444 /* Set the pshared attributes, if supported */
445 if ((pshared > 0) && (scenarii[scenar].mc_pshared != 0)) {
446 ret = pthread_mutexattr_setpshared(&ma,
447 PTHREAD_PROCESS_SHARED);
448 if (ret != 0)
449 UNRESOLVED(ret, "[parent] Unable to set the "
450 "mutex process-shared");
451 ret = pthread_condattr_setpshared(&ca,
452 PTHREAD_PROCESS_SHARED);
453 if (ret != 0)
454 UNRESOLVED(ret, "[parent] Unable to set the "
455 "cond var process-shared");
456 }
457
458 /* Set the alternative clock, if supported */
459 #ifdef USE_ALTCLK
460 if ((cs > 0) && (scenarii[scenar].c_clock != 0)) {
461 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
462 if (ret != 0)
463 UNRESOLVED(ret, "[parent] Unable to set the "
464 "monotonic clock for the cond");
465 }
466 ret = pthread_condattr_getclock(&ca, &td->cid);
467 if (ret != 0)
468 UNRESOLVED(ret, "Unable to get clock from cond attr");
469 #else
470 td->cid = CLOCK_REALTIME;
471 #endif
472
473 /* Tell whether the test will be across processes */
474 if ((pshared > 0) && (scenarii[scenar].fork != 0))
475 td->fork = 1;
476
477 /* initialize the condvar */
478 ret = pthread_cond_init(&td->cnd, &ca);
479 if (ret != 0)
480 UNRESOLVED(ret, "Cond init failed");
481
482 /* initialize the mutex */
483 ret = pthread_mutex_init(&td->mtx, &ma);
484 if (ret != 0)
485 UNRESOLVED(ret, "Mutex init failed");
486
487 /* Destroy the attributes */
488 ret = pthread_condattr_destroy(&ca);
489 if (ret != 0)
490 UNRESOLVED(ret,
491 "Failed to destroy the cond var "
492 "attribute object");
493
494 ret = pthread_mutexattr_destroy(&ma);
495 if (ret != 0)
496 UNRESOLVED(ret,
497 "Failed to destroy the mutex "
498 "attribute object");
499
500 #if VERBOSE > 2
501 output("[parent] Starting test %s\n", scenarii[scenar].descr);
502 #endif
503
504 td->count = 0;
505 child_count = 0;
506 cur = children;
507
508 /* create the timeout thread */
509 ret = pthread_create(&t_timer, NULL, timer, NULL);
510 if (ret != 0)
511 UNRESOLVED_KILLALL(ret,
512 "Unable to create timer thread");
513
514 /* Create all the children */
515 if (td->fork == 0) {
516 do {
517 tmp = malloc(sizeof(children_t));
518 if (tmp != NULL) {
519 ret = pthread_create(&(tmp->data.t),
520 &ta, child, NULL);
521 if (ret != 0) {
522 free(tmp);
523 } else {
524 child_count++;
525 tmp->next = NULL;
526 cur->next = tmp;
527 cur = tmp;
528 }
529 } else {
530 ret = errno;
531 }
532 } while ((ret == 0)
533 && (child_count < max_thread_children));
534 #if VERBOSE > 2
535 output("[parent] Created %i children threads\n",
536 child_count);
537 #endif
538 if (child_count == 0)
539 UNRESOLVED(ret, "Unable to create any thread");
540 } else {
541 do {
542 tmp = malloc(sizeof(children_t));
543 if (tmp != NULL) {
544 tmp->data.p = fork();
545 if (tmp->data.p == 0) {
546 child(NULL);
547 exit(0);
548 }
549 if (tmp->data.p == -1) {
550 ret = errno;
551 free(tmp);
552 } else {
553 ret = 0;
554 child_count++;
555 tmp->next = NULL;
556 cur->next = tmp;
557 cur = tmp;
558 }
559 } else {
560 ret = errno;
561 }
562 } while ((ret == 0)
563 && (child_count < max_process_children));
564 #if VERBOSE > 2
565 output("[parent] Created %i children processes\n",
566 child_count);
567 #endif
568 if (child_count == 0)
569 UNRESOLVED(ret, "Unable to create any process");
570
571 }
572
573 /* Make sure all children are waiting */
574 ret = pthread_mutex_lock(&td->mtx);
575 if (ret != 0)
576 UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
577 ch = td->count;
578 while (ch < child_count) {
579 ret = pthread_mutex_unlock(&td->mtx);
580 if (ret != 0)
581 UNRESOLVED_KILLALL(ret,
582 "Failed to unlock mutex");
583 sched_yield();
584 ret = pthread_mutex_lock(&td->mtx);
585 if (ret != 0)
586 UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
587 ch = td->count;
588 }
589
590 #if VERBOSE > 4
591 output("[parent] All children are waiting\n");
592 #endif
593
594 /* start the timer count */
595 do {
596 ret = sem_post(&sem_tmr);
597 } while ((ret != 0) && (errno == EINTR));
598 if (ret != 0)
599 UNRESOLVED_KILLALL(errno,
600 "Failed to post the semaphore.");
601
602 /* Wakeup the children */
603 td->predicate = 1;
604 ret = pthread_cond_broadcast(&td->cnd);
605 if (ret != 0)
606 UNRESOLVED_KILLALL(ret,
607 "Failed to broadcast the condition.");
608
609 #if VERBOSE > 4
610 output("[parent] Condition was signaled\n");
611 #endif
612
613 ret = pthread_mutex_unlock(&td->mtx);
614 if (ret != 0)
615 UNRESOLVED_KILLALL(ret, "Failed to unlock mutex");
616
617 #if VERBOSE > 4
618 output("[parent] Joining the children\n");
619 #endif
620
621 /* join the children */
622 while (children->next != NULL) {
623 tmp = children->next;
624 children->next = tmp->next;
625 if (td->fork == 0) {
626 ret |= pthread_join(tmp->data.t, NULL);
627 } else {
628 pid = waitpid(tmp->data.p, &status, 0);
629 if (pid != tmp->data.p) {
630 ret = errno;
631 output("Waitpid failed (expected: %i, "
632 "got: %i)\n", tmp->data.p, pid);
633 free(tmp);
634 UNRESOLVED_KILLALL(ret,
635 "Waitpid failed");
636 }
637 if (WIFEXITED(status)) {
638 /* the child should return only failed or unresolved or passed */
639 if (ret != PTS_FAIL)
640 ret |= WEXITSTATUS(status);
641 }
642 }
643 free(tmp);
644 }
645 if (ret != 0) {
646 output_fini();
647 exit(ret);
648 }
649 #if VERBOSE > 4
650 output("[parent] All children terminated\n");
651 #endif
652
653 /* cancel the timeout thread */
654 ret = pthread_cancel(t_timer);
655 if (ret != 0)
656 /* Strange error here... the thread cannot be terminated
657 * (app would be killed)
658 */
659 UNRESOLVED(ret, "Failed to cancel the timeout handler");
660
661 /* join the timeout thread */
662 ret = pthread_join(t_timer, NULL);
663 if (ret != 0)
664 UNRESOLVED(ret, "Failed to join the timeout handler");
665
666 /* Destroy the datas */
667 ret = pthread_cond_destroy(&td->cnd);
668 if (ret != 0)
669 UNRESOLVED(ret, "Failed to destroy the condvar");
670
671 ret = pthread_mutex_destroy(&td->mtx);
672 if (ret != 0)
673 UNRESOLVED(ret, "Failed to destroy the mutex");
674
675 }
676
677 /* Destroy global data */
678 ret = pthread_attr_destroy(&ta);
679 if (ret != 0)
680 UNRESOLVED(ret, "Final thread attr destroy failed");
681
682 ret = sem_destroy(&sem_tmr);
683 if (ret != 0)
684 UNRESOLVED(errno, "Final semaphore destroy failed");
685
686 /* exit */
687 PASSED;
688 }
689