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 a cancel request unblocks the thread,
21 * it must not consume any pending condition signal request.
22
23 * The steps are:
24 * -> Create a bunch of threads waiting on a condvar.
25 * -> At the same time (using a barrier) one thread is canceled and the condition is signaled.
26 * -> Test checks that the cond signaling was not lost (at least one thread must have woken cleanly).
27 * -> Then everything is cleaned up and started again.
28
29 */
30
31 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
32 #define _POSIX_C_SOURCE 200112L
33
34 /* We need the XSI extention for the mutex attributes */
35 #ifndef WITHOUT_XOPEN
36 #define _XOPEN_SOURCE 600
37 #endif
38 /********************************************************************************************/
39 /****************************** standard includes *****************************************/
40 /********************************************************************************************/
41 #include <pthread.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46
47 #include <errno.h>
48 #include <signal.h>
49 #include <string.h>
50 #include <time.h>
51
52 /********************************************************************************************/
53 /****************************** Test framework *****************************************/
54 /********************************************************************************************/
55 #include "testfrmw.h"
56 #include "testfrmw.c"
57 /* This header is responsible for defining the following macros:
58 * UNRESOLVED(ret, descr);
59 * where descr is a description of the error and ret is an int (error code for example)
60 * FAILED(descr);
61 * where descr is a short text saying why the test has failed.
62 * PASSED();
63 * No parameter.
64 *
65 * Both three macros shall terminate the calling process.
66 * The testcase shall not terminate in any other maneer.
67 *
68 * The other file defines the functions
69 * void output_init()
70 * void output(char * string, ...)
71 *
72 * Those may be used to output information.
73 */
74
75 /********************************************************************************************/
76 /********************************** Configuration ******************************************/
77 /********************************************************************************************/
78 #ifndef SCALABILITY_FACTOR
79 #define SCALABILITY_FACTOR 1
80 #endif
81 #ifndef VERBOSE
82 #define VERBOSE 1
83 #endif
84
85 /* Size of the "bunch" of threads -- the real number will be 2 more threads per scenarii */
86 #define NCHILDREN (20)
87
88 #define TIMEOUT (60)
89
90 #ifndef WITHOUT_ALTCLK
91 #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */
92 #endif
93
94 /********************************************************************************************/
95 /*********************************** Test case *****************************************/
96 /********************************************************************************************/
97
98 #ifdef WITHOUT_XOPEN
99 /* We define those to avoid compilation errors, but they won't be used */
100 #define PTHREAD_MUTEX_DEFAULT 0
101 #define PTHREAD_MUTEX_NORMAL 0
102 #define PTHREAD_MUTEX_ERRORCHECK 0
103 #define PTHREAD_MUTEX_RECURSIVE 0
104
105 #endif
106
107 struct _scenar {
108 int m_type; /* Mutex type to use */
109 int mc_pshared; /* 0: mutex and cond are process-private (default) ~ !0: Both are process-shared, if supported */
110 int c_clock; /* 0: cond uses the default clock. ~ !0: Cond uses monotonic clock, if supported. */
111 int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */
112 char *descr; /* Case description */
113 } scenarii[] = {
114 {
115 PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}
116 , {
117 PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}
118 , {
119 PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}
120 , {
121 PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}
122
123 , {
124 PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}
125 , {
126 PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}
127 , {
128 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}
129 , {
130 PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}
131
132 , {
133 PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
134 "Pshared default mutex across processes"}
135 , {
136 PTHREAD_MUTEX_NORMAL, 1, 0, 1,
137 "Pshared normal mutex across processes"}
138 , {
139 PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
140 "Pshared errorcheck mutex across processes"}
141 , {
142 PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
143 "Pshared recursive mutex across processes"}
144
145 #ifdef USE_ALTCLK
146 , {
147 PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
148 "Pshared default mutex and alt clock condvar across processes"}
149 , {
150 PTHREAD_MUTEX_NORMAL, 1, 1, 1,
151 "Pshared normal mutex and alt clock condvar across processes"}
152 , {
153 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
154 "Pshared errorcheck mutex and alt clock condvar across processes"}
155 , {
156 PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
157 "Pshared recursive mutex and alt clock condvar across processes"}
158
159 , {
160 PTHREAD_MUTEX_DEFAULT, 0, 1, 0,
161 "Default mutex and alt clock condvar"}
162 , {
163 PTHREAD_MUTEX_NORMAL, 0, 1, 0,
164 "Normal mutex and alt clock condvar"}
165 , {
166 PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
167 "Errorcheck mutex and alt clock condvar"}
168 , {
169 PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
170 "Recursive mutex and alt clock condvar"}
171
172 , {
173 PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
174 "PShared default mutex and alt clock condvar"}
175 , {
176 PTHREAD_MUTEX_NORMAL, 1, 1, 0,
177 "Pshared normal mutex and alt clock condvar"}
178 , {
179 PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
180 "Pshared errorcheck mutex and alt clock condvar"}
181 , {
182 PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
183 "Pshared recursive mutex and alt clock condvar"}
184 #endif
185 };
186
187 #define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
188
189 /* This is the shared structure for all threads related to the same condvar */
190 struct celldata {
191 pthread_t workers[NCHILDREN * SCALABILITY_FACTOR + 2];
192 pthread_t signaler;
193
194 pthread_barrier_t bar;
195 pthread_mutex_t mtx;
196 pthread_cond_t cnd;
197 clockid_t cid;
198
199 int boolean;
200 int count;
201
202 long canceled;
203 long cancelfailed;
204 long cnttotal;
205 } cells[NSCENAR * SCALABILITY_FACTOR];
206
207 char do_it = 1;
208 pthread_attr_t ta;
209
cleanup(void * arg)210 void cleanup(void *arg)
211 {
212 int ret;
213 struct celldata *cd = (struct celldata *)arg;
214
215 /* Unlock the mutex */
216 ret = pthread_mutex_unlock(&(cd->mtx));
217 if (ret != 0) {
218 UNRESOLVED(ret, "Failed to unlock mutex in cancel handler");
219 }
220
221 }
222
worker(void * arg)223 void *worker(void *arg)
224 {
225 int ret;
226 struct celldata *cd = (struct celldata *)arg;
227 struct timespec ts;
228
229 /* lock the mutex */
230 ret = pthread_mutex_lock(&(cd->mtx));
231 if (ret != 0) {
232 UNRESOLVED(ret, "Unable to lock mutex in worker");
233 }
234
235 /* Tell the cellmaster we are ready (count++) */
236 cd->count += 1;
237
238 /* Timeout = now + TIMEOUT */
239 ret = clock_gettime(cd->cid, &ts);
240 if (ret != 0) {
241 UNRESOLVED(errno, "Gettime failed");
242 }
243 ts.tv_sec += TIMEOUT * SCALABILITY_FACTOR;
244
245 /* register cleanup handler */
246 pthread_cleanup_push(cleanup, arg);
247
248 do {
249 /* cond timedwait (while boolean == false) */
250 ret = pthread_cond_timedwait(&(cd->cnd), &(cd->mtx), &ts);
251
252 /* if timeout => failed (lost signal) */
253 if (ret == ETIMEDOUT) {
254 FAILED
255 ("Timeout occured. A condition signal was probably lost.");
256 }
257
258 if (ret != 0) {
259 UNRESOLVED(ret, "Cond timedwait failed");
260 }
261
262 } while (cd->boolean == 0);
263
264 /* broadcast the condition */
265 ret = pthread_cond_broadcast(&(cd->cnd));
266 if (ret != 0) {
267 UNRESOLVED(ret, "Broadcasting the condition failed");
268 }
269
270 /* unregister the cleanup */
271 pthread_cleanup_pop(0);
272
273 /* unlock the mutex */
274 ret = pthread_mutex_unlock(&(cd->mtx));
275 if (ret != 0) {
276 UNRESOLVED(ret, "Unable to unlock the mutex");
277 }
278
279 return NULL;
280 }
281
signaler(void * arg)282 void *signaler(void *arg)
283 {
284 int ret;
285 struct celldata *cd = (struct celldata *)arg;
286
287 /* Lock the mutex if required */
288 if (cd->boolean == -1) {
289 ret = pthread_mutex_lock(&(cd->mtx));
290 if (ret != 0) {
291 UNRESOLVED(ret, "mutex lock failed in signaler");
292 }
293 }
294
295 /* wait the barrier */
296 ret = pthread_barrier_wait(&(cd->bar));
297 if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {
298 UNRESOLVED(ret, "Barrier wait failed");
299 }
300
301 /* signal the cond */
302 ret = pthread_cond_signal(&(cd->cnd));
303 if (ret != 0) {
304 UNRESOLVED(ret, "Signaling the cond failed");
305 }
306
307 /* Unlock the mutex if required */
308 if (cd->boolean == -1) {
309 ret = pthread_mutex_unlock(&(cd->mtx));
310 if (ret != 0) {
311 UNRESOLVED(ret, "mutex unlock failed in signaler");
312 }
313 }
314
315 return NULL;
316 }
317
cellmanager(void * arg)318 void *cellmanager(void *arg)
319 {
320 int ret, i;
321 struct celldata *cd = (struct celldata *)arg;
322 struct timespec ts;
323 int randval;
324 void *w_ret;
325
326 cd->canceled = 0;
327 cd->cancelfailed = 0;
328 cd->cnttotal = 0;
329
330 /* while do_it */
331 while (do_it) {
332 /* Initialize some stuff */
333 cd->boolean = 0;
334 cd->count = 0;
335 cd->cnttotal += 1;
336
337 /* create the workers */
338 for (i = 0; i < NCHILDREN * SCALABILITY_FACTOR + 2; i++) {
339 ret =
340 pthread_create(&(cd->workers[i]), &ta, worker, arg);
341 if (ret != 0) {
342 UNRESOLVED(ret,
343 "Unable to create enough threads");
344 }
345 }
346
347 /* choose a (pseudo) random thread to cancel */
348 ret = clock_gettime(cd->cid, &ts);
349 if (ret != 0) {
350 UNRESOLVED(errno, "Failed to read clock");
351 }
352 randval =
353 (ts.tv_sec +
354 (ts.tv_nsec >> 10)) % (NCHILDREN * SCALABILITY_FACTOR + 2);
355
356 /* wait for the workers to be ready */
357 do {
358 ret = pthread_mutex_lock(&(cd->mtx));
359 if (ret != 0) {
360 UNRESOLVED(ret, "Mutex lock failed");
361 }
362
363 i = cd->count;
364
365 ret = pthread_mutex_unlock(&(cd->mtx));
366 if (ret != 0) {
367 UNRESOLVED(ret, "Mutex unlock failed");
368 }
369 } while (i < NCHILDREN * SCALABILITY_FACTOR + 2);
370
371 /* Set the boolean (1 => no lock in signaler; -1 => lock) */
372 cd->boolean = (ts.tv_sec & 1) ? -1 : 1;
373
374 /* create the signaler */
375 ret = pthread_create(&(cd->signaler), &ta, signaler, arg);
376 if (ret != 0) {
377 UNRESOLVED(ret, "Failed to create signaler thread");
378 }
379
380 /* wait the barrier */
381 ret = pthread_barrier_wait(&(cd->bar));
382 if ((ret != 0) && (ret != PTHREAD_BARRIER_SERIAL_THREAD)) {
383 UNRESOLVED(ret, "Failed to wait for the barrier");
384 }
385
386 /* cancel the chosen thread */
387 ret = pthread_cancel(cd->workers[randval]);
388
389 /* it is possible the thread is already terminated -- so we don't stop on error */
390 if (ret != 0) {
391 #if VERBOSE > 2
392 output("%d\n", randval);
393 #endif
394 cd->cancelfailed += 1;
395 }
396
397 /* join every threads */
398 ret = pthread_join(cd->signaler, NULL);
399 if (ret != 0) {
400 UNRESOLVED(ret, "Failed to join the signaler thread");
401 }
402
403 for (i = 0; i < NCHILDREN * SCALABILITY_FACTOR + 2; i++) {
404 ret = pthread_join(cd->workers[i], &w_ret);
405 if (ret != 0) {
406 UNRESOLVED(ret, "Unable to join a worker");
407 }
408 if (w_ret == PTHREAD_CANCELED)
409 cd->canceled += 1;
410 }
411 }
412
413 return NULL;
414 }
415
sighdl(int sig)416 void sighdl(int sig)
417 {
418 /* do_it = 0 */
419 do {
420 do_it = 0;
421 }
422 while (do_it);
423 }
424
main(int argc,char * argv[])425 int main(int argc, char *argv[])
426 {
427 int ret, i, j;
428 struct sigaction sa;
429
430 pthread_mutexattr_t ma;
431 pthread_condattr_t ca;
432 clockid_t cid = CLOCK_REALTIME;
433 long canceled = 0;
434 long cancelfailed = 0;
435 long cnttotal = 0;
436
437 long pshared, monotonic, cs;
438
439 pthread_t mngrs[NSCENAR * SCALABILITY_FACTOR];
440
441 output_init();
442
443 /* check the system abilities */
444 pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
445 cs = sysconf(_SC_CLOCK_SELECTION);
446 monotonic = sysconf(_SC_MONOTONIC_CLOCK);
447
448 #if VERBOSE > 0
449 output("Test starting\n");
450 output("System abilities:\n");
451 output(" TPS : %li\n", pshared);
452 output(" CS : %li\n", cs);
453 output(" MON : %li\n", monotonic);
454 if ((cs < 0) || (monotonic < 0))
455 output("Alternative clock won't be tested\n");
456 #endif
457
458 if (monotonic < 0)
459 cs = -1;
460
461 #ifndef USE_ALTCLK
462 if (cs > 0)
463 output
464 ("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
465 #endif
466
467 /* Initialize the celldatas according to scenarii */
468 for (i = 0; i < NSCENAR; i++) {
469 #if VERBOSE > 1
470 output("[parent] Preparing attributes for: %s\n",
471 scenarii[i].descr);
472 #ifdef WITHOUT_XOPEN
473 output("[parent] Mutex attributes DISABLED -> not used\n");
474 #endif
475 #endif
476
477 /* set / reset everything */
478 ret = pthread_mutexattr_init(&ma);
479 if (ret != 0) {
480 UNRESOLVED(ret,
481 "[parent] Unable to initialize the mutex attribute object");
482 }
483 ret = pthread_condattr_init(&ca);
484 if (ret != 0) {
485 UNRESOLVED(ret,
486 "[parent] Unable to initialize the cond attribute object");
487 }
488 #ifndef WITHOUT_XOPEN
489 /* Set the mutex type */
490 ret = pthread_mutexattr_settype(&ma, scenarii[i].m_type);
491 if (ret != 0) {
492 UNRESOLVED(ret, "[parent] Unable to set mutex type");
493 }
494 #if VERBOSE > 1
495 output("[parent] Mutex type : %i\n", scenarii[i].m_type);
496 #endif
497 #endif
498
499 /* Set the pshared attributes, if supported */
500 if ((pshared > 0) && (scenarii[i].mc_pshared != 0)) {
501 ret =
502 pthread_mutexattr_setpshared(&ma,
503 PTHREAD_PROCESS_SHARED);
504 if (ret != 0) {
505 UNRESOLVED(ret,
506 "[parent] Unable to set the mutex process-shared");
507 }
508 ret =
509 pthread_condattr_setpshared(&ca,
510 PTHREAD_PROCESS_SHARED);
511 if (ret != 0) {
512 UNRESOLVED(ret,
513 "[parent] Unable to set the cond var process-shared");
514 }
515 #if VERBOSE > 1
516 output("[parent] Mutex & cond are process-shared\n");
517 #endif
518 }
519 #if VERBOSE > 1
520 else {
521 output("[parent] Mutex & cond are process-private\n");
522 }
523 #endif
524
525 /* Set the alternative clock, if supported */
526 #ifdef USE_ALTCLK
527 if ((cs > 0) && (scenarii[i].c_clock != 0)) {
528 ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
529 if (ret != 0) {
530 UNRESOLVED(ret,
531 "[parent] Unable to set the monotonic clock for the cond");
532 }
533 #if VERBOSE > 1
534 output("[parent] Cond uses the Monotonic clock\n");
535 #endif
536 }
537 #if VERBOSE > 1
538 else {
539 output("[parent] Cond uses the default clock\n");
540 }
541 #endif
542 ret = pthread_condattr_getclock(&ca, &cid);
543 if (ret != 0) {
544 UNRESOLVED(ret, "Unable to get clock from cond attr");
545 }
546 #endif
547
548 /* Initialize all the mutex and condvars which uses those attributes */
549 for (j = 0; j < SCALABILITY_FACTOR; j++) {
550 cells[i + j * NSCENAR].cid = cid;
551
552 /* initialize the condvar */
553 ret =
554 pthread_cond_init(&(cells[i + j * NSCENAR].cnd),
555 &ca);
556 if (ret != 0) {
557 UNRESOLVED(ret, "Cond init failed");
558 }
559
560 /* initialize the mutex */
561 ret =
562 pthread_mutex_init(&(cells[i + j * NSCENAR].mtx),
563 &ma);
564 if (ret != 0) {
565 UNRESOLVED(ret, "Mutex init failed");
566 }
567
568 /* initialize the barrier */
569 ret =
570 pthread_barrier_init(&(cells[i + j * NSCENAR].bar),
571 NULL, 2);
572 if (ret != 0) {
573 UNRESOLVED(ret, "Failed to init barrier");
574 }
575 }
576
577 ret = pthread_condattr_destroy(&ca);
578 if (ret != 0) {
579 UNRESOLVED(ret,
580 "Failed to destroy the cond var attribute object");
581 }
582
583 ret = pthread_mutexattr_destroy(&ma);
584 if (ret != 0) {
585 UNRESOLVED(ret,
586 "Failed to destroy the mutex attribute object");
587 }
588 }
589 #if VERBOSE > 1
590 output("[parent] All condvars & mutex are ready\n");
591 #endif
592
593 /* register the signal handler */
594 sigemptyset(&sa.sa_mask);
595 sa.sa_flags = 0;
596 sa.sa_handler = sighdl;
597 if ((ret = sigaction(SIGUSR1, &sa, NULL))) {
598 UNRESOLVED(ret, "Unable to register signal handler");
599 }
600 #if VERBOSE > 1
601 output("[parent] Signal handler registered\n");
602 #endif
603
604 /* Initialize the thread attribute object */
605 ret = pthread_attr_init(&ta);
606 if (ret != 0) {
607 UNRESOLVED(ret,
608 "[parent] Failed to initialize a thread attribute object");
609 }
610 ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
611 if (ret != 0) {
612 UNRESOLVED(ret, "[parent] Failed to set thread stack size");
613 }
614
615 /* create the NSCENAR * SCALABILITY_FACTOR manager threads */
616 for (i = 0; i < NSCENAR * SCALABILITY_FACTOR; i++) {
617 ret = pthread_create(&mngrs[i], &ta, cellmanager, &(cells[i]));
618 /* In case of failure we can exit; the child process will die after a while */
619 if (ret != 0) {
620 UNRESOLVED(ret, "[Parent] Failed to create a thread");
621 }
622 #if VERBOSE > 1
623 if ((i % 4) == 0)
624 output("[parent] %i manager threads created...\n",
625 i + 1);
626 #endif
627 }
628
629 #if VERBOSE > 1
630 output("[parent] All %i manager threads are running...\n",
631 NSCENAR * SCALABILITY_FACTOR);
632 #endif
633
634 /* join the manager threads and destroy the cells */
635 for (i = 0; i < NSCENAR * SCALABILITY_FACTOR; i++) {
636 ret = pthread_join(mngrs[i], NULL);
637 if (ret != 0) {
638 UNRESOLVED(ret, "[Parent] Failed to join a thread");
639 }
640
641 canceled += cells[i].canceled;
642 cancelfailed += cells[i].cancelfailed;
643 cnttotal += cells[i].cnttotal;
644
645 ret = pthread_barrier_destroy(&(cells[i].bar));
646 if (ret != 0) {
647 UNRESOLVED(ret, "Failed to destroy a barrier");
648 }
649
650 ret = pthread_cond_destroy(&(cells[i].cnd));
651 if (ret != 0) {
652 UNRESOLVED(ret, "Failed to destroy a cond");
653 }
654
655 ret = pthread_mutex_destroy(&(cells[i].mtx));
656 if (ret != 0) {
657 UNRESOLVED(ret, "Failed to destroy a mutex");
658 }
659 }
660
661 /* exit */
662 #if VERBOSE > 0
663 output("Test passed\n");
664 output(" Total loops : %8li\n", cnttotal);
665 #endif
666 #if VERBOSE > 1
667 output(" Failed cancel request: %8li\n", cancelfailed);
668 output(" Canceled threads : %8li\n", canceled);
669 #endif
670
671 PASSED;
672 }
673