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