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
18 * This file is a stress test for the pthread_mutex_lock function.
19
20 * The steps are:
21 * -> For each king of mutex, we create 10*F threads (F is a scalability factor)
22 * -> we call those threads 1 to 10.
23 * -> thread 1 sends signal USR2 to the other 9 threads (which have a handler for it)
24 * -> thread 2 to 6 are loops
25 * {
26 * mutex_lock
27 * if (ctrl) exit
28 * ctrl = 1
29 * yield
30 * ctrl= 0
31 * mutex unlock
32 * }
33 * -> thread 7 & 8 have a timedlock instead of lock
34 * -> thread 9 & 10 have a trylock instead of lock
35 *
36 * -> the whole process stop when receiving signal SIGUSR1.
37 * This goal is achieved with a "do_it" variable.
38 *
39 * NOTE: With gcc/linux, the flag "-lrt" must be specified at link time.
40 */
41
42 /********************************************************************************************/
43 /****************************** standard includes *****************************************/
44 /********************************************************************************************/
45 #include <pthread.h>
46 #include <errno.h>
47 #include <semaphore.h>
48 #include <signal.h>
49 #include <unistd.h>
50 #if _POSIX_TIMEOUTS < 0
51 #error "This sample needs POSIX TIMEOUTS option support"
52 #endif
53 #if _POSIX_TIMEOUTS == 0
54 #warning "This sample needs POSIX TIMEOUTS option support"
55 #endif
56 #if _POSIX_TIMERS < 0
57 #error "This sample needs POSIX TIMERS option support"
58 #endif
59 #if _POSIX_TIMERS == 0
60 #warning "This sample needs POSIX TIMERS option support"
61 #endif
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <stdarg.h>
66 #include <time.h> /* required for the pthread_mutex_timedlock() function */
67
68 /********************************************************************************************/
69 /****************************** Test framework *****************************************/
70 /********************************************************************************************/
71 #include "testfrmw.h"
72 #include "testfrmw.c"
73 /* This header is responsible for defining the following macros:
74 * UNRESOLVED(ret, descr);
75 * where descr is a description of the error and ret is an int (error code for example)
76 * FAILED(descr);
77 * where descr is a short text saying why the test has failed.
78 * PASSED();
79 * No parameter.
80 *
81 * Both three macros shall terminate the calling process.
82 * The testcase shall not terminate in any other maneer.
83 *
84 * The other file defines the functions
85 * void output_init()
86 * void output(char * string, ...)
87 *
88 * Those may be used to output information.
89 */
90
91 /********************************************************************************************/
92 /********************************** Configuration ******************************************/
93 /********************************************************************************************/
94 #ifndef SCALABILITY_FACTOR
95 #define SCALABILITY_FACTOR 1
96 #endif
97 #ifndef VERBOSE
98 #define VERBOSE 2
99 #endif
100 #define N 2 /* N * 10 * 6 * SCALABILITY_FACTOR threads will be created */
101
102 /********************************************************************************************/
103 /*********************************** Test case *****************************************/
104 /********************************************************************************************/
105 static char do_it = 1;
106 #ifndef WITHOUT_XOPEN
107 static int types[] = { PTHREAD_MUTEX_NORMAL,
108 PTHREAD_MUTEX_ERRORCHECK,
109 PTHREAD_MUTEX_RECURSIVE,
110 PTHREAD_MUTEX_DEFAULT
111 };
112 #endif
113
114 /* The following type represents the data
115 * for one group of ten threads */
116 typedef struct {
117 pthread_t threads[10]; /* The 10 threads */
118 pthread_mutex_t mtx; /* The mutex those threads work on */
119 char ctrl; /* The value used to check the behavior */
120 char sigok; /* Used to tell the threads they can return */
121 sem_t semsig; /* Semaphore for synchronizing the signal handler */
122 int id; /* An identifier for the threads group */
123 int tcnt; /* we need to make sure the threads are started before killing 'em */
124 pthread_mutex_t tmtx;
125 unsigned long long sigcnt, opcnt; /* We count every iteration */
126 } cell_t;
127
128 static pthread_key_t _c; /* this key will always contain a pointer to the thread's cell */
129
130 /***** The next function is in charge of sending signal USR2 to
131 * all the other threads in its cell, until the end of the test. */
sigthr(void * arg)132 static void *sigthr(void *arg)
133 {
134 int ret;
135 int i = 0;
136 cell_t *c = (cell_t *) arg;
137
138 do {
139 sched_yield();
140 ret = pthread_mutex_lock(&(c->tmtx));
141 if (ret != 0) {
142 UNRESOLVED(ret, "Failed to lock the mutex");
143 }
144 i = c->tcnt;
145 ret = pthread_mutex_unlock(&(c->tmtx));
146 if (ret != 0) {
147 UNRESOLVED(ret, "Failed to unlock the mutex");
148 }
149 } while (i < 9);
150
151 /* Until we must stop, do */
152 while (do_it) {
153 /* Wait for the semaphore */
154 ret = sem_wait(&(c->semsig));
155 if (ret != 0) {
156 UNRESOLVED(errno, "Sem wait failed in signal thread");
157 }
158
159 /* Kill the next thread */
160 i %= 9;
161 ret = pthread_kill(c->threads[++i], SIGUSR2);
162 if (ret != 0) {
163 UNRESOLVED(ret, "Thread kill failed in signal thread");
164 }
165
166 /* Increment the signal counter */
167 c->sigcnt++;
168 }
169
170 /* Tell the other threads they can now stop */
171 do {
172 c->sigok = 1;
173 }
174 while (c->sigok == 0);
175
176 return NULL;
177 }
178
179 /***** The next function is the signal handler
180 * for all the others threads in the cell */
sighdl(int sig)181 static void sighdl(int sig)
182 {
183 int ret;
184 cell_t *c = (cell_t *) pthread_getspecific(_c);
185 ret = sem_post(&(c->semsig));
186 if (ret != 0) {
187 UNRESOLVED(errno, "Unable to post semaphore in signal handler");
188 }
189 }
190
191 /***** The next function can return only when the sigthr has terminated.
192 * This avoids the signal thread try to kill a terminated thread. */
waitsigend(cell_t * c)193 static void waitsigend(cell_t * c)
194 {
195 while (c->sigok == 0) {
196 sched_yield();
197 }
198 }
199
200 /***** The next function aims to control that no other thread
201 * owns the mutex at the same time */
control(cell_t * c,char * loc)202 static void control(cell_t * c, char *loc)
203 {
204 *loc++; /* change the local control value */
205 if (c->ctrl != 0) {
206 FAILED("Got a non-zero value - two threads owns the mutex");
207 }
208 c->ctrl = *loc;
209 sched_yield();
210 if (c->ctrl != *loc) {
211 FAILED
212 ("Got a different value - another thread touched protected data");
213 }
214 c->ctrl = 0;
215
216 /* Avoid some values for the next control */
217 if (*loc == 120)
218 *loc = -120;
219 if (*loc == -1)
220 *loc = 1;
221 }
222
223 /***** The next 3 functions are the worker threads
224 */
lockthr(void * arg)225 static void *lockthr(void *arg)
226 {
227 int ret;
228 char loc; /* Local value for control */
229 cell_t *c = (cell_t *) arg;
230
231 /* Set the thread local data key value (used in the signal handler) */
232 ret = pthread_setspecific(_c, arg);
233 if (ret != 0) {
234 UNRESOLVED(ret, "Unable to assign the thread-local-data key");
235 }
236
237 /* Signal we're started */
238 ret = pthread_mutex_lock(&(c->tmtx));
239 if (ret != 0) {
240 UNRESOLVED(ret, "Failed to lock the mutex");
241 }
242 c->tcnt += 1;
243 ret = pthread_mutex_unlock(&(c->tmtx));
244 if (ret != 0) {
245 UNRESOLVED(ret, "Failed to unlock the mutex");
246 }
247
248 do {
249 /* Lock, control, then unlock */
250 ret = pthread_mutex_lock(&(c->mtx));
251 if (ret != 0) {
252 UNRESOLVED(ret, "Mutex lock failed in worker thread");
253 }
254
255 control(c, &loc);
256
257 ret = pthread_mutex_unlock(&(c->mtx));
258 if (ret != 0) {
259 UNRESOLVED(ret, "Mutex unlock failed in worker thread");
260 }
261
262 /* Increment the operation counter */
263 c->opcnt++;
264 }
265 while (do_it);
266
267 /* Wait for the signal thread to terminate before we can exit */
268 waitsigend(c);
269 return NULL;
270 }
271
timedlockthr(void * arg)272 static void *timedlockthr(void *arg)
273 {
274 int ret;
275 char loc; /* Local value for control */
276 struct timespec ts;
277 cell_t *c = (cell_t *) arg;
278
279 /* Set the thread local data key value (used in the signal handler) */
280 ret = pthread_setspecific(_c, arg);
281 if (ret != 0) {
282 UNRESOLVED(ret, "Unable to assign the thread-local-data key");
283 }
284
285 /* Signal we're started */
286 ret = pthread_mutex_lock(&(c->tmtx));
287 if (ret != 0) {
288 UNRESOLVED(ret, "Failed to lock the mutex");
289 }
290 c->tcnt += 1;
291 ret = pthread_mutex_unlock(&(c->tmtx));
292 if (ret != 0) {
293 UNRESOLVED(ret, "Failed to unlock the mutex");
294 }
295
296 do {
297 /* Lock, control, then unlock */
298 do {
299 ret = clock_gettime(CLOCK_REALTIME, &ts);
300 if (ret != 0) {
301 UNRESOLVED(errno,
302 "Unable to get time for timeout");
303 }
304 ts.tv_sec++; /* We will wait for 1 second */
305 ret = pthread_mutex_timedlock(&(c->mtx), &ts);
306 } while (ret == ETIMEDOUT);
307 if (ret != 0) {
308 UNRESOLVED(ret,
309 "Timed mutex lock failed in worker thread");
310 }
311
312 control(c, &loc);
313
314 ret = pthread_mutex_unlock(&(c->mtx));
315 if (ret != 0) {
316 UNRESOLVED(ret, "Mutex unlock failed in worker thread");
317 }
318
319 /* Increment the operation counter */
320 c->opcnt++;
321 }
322 while (do_it);
323
324 /* Wait for the signal thread to terminate before we can exit */
325 waitsigend(c);
326 return NULL;
327 }
328
trylockthr(void * arg)329 static void *trylockthr(void *arg)
330 {
331 int ret;
332 char loc; /* Local value for control */
333 cell_t *c = (cell_t *) arg;
334
335 /* Set the thread local data key value (used in the signal handler) */
336 ret = pthread_setspecific(_c, arg);
337 if (ret != 0) {
338 UNRESOLVED(ret, "Unable to assign the thread-local-data key");
339 }
340
341 /* Signal we're started */
342 ret = pthread_mutex_lock(&(c->tmtx));
343 if (ret != 0) {
344 UNRESOLVED(ret, "Failed to lock the mutex");
345 }
346 c->tcnt += 1;
347 ret = pthread_mutex_unlock(&(c->tmtx));
348 if (ret != 0) {
349 UNRESOLVED(ret, "Failed to unlock the mutex");
350 }
351
352 do {
353 /* Lock, control, then unlock */
354 do {
355 ret = pthread_mutex_trylock(&(c->mtx));
356 } while (ret == EBUSY);
357 if (ret != 0) {
358 UNRESOLVED(ret,
359 "Mutex lock try failed in worker thread");
360 }
361
362 control(c, &loc);
363
364 ret = pthread_mutex_unlock(&(c->mtx));
365 if (ret != 0) {
366 UNRESOLVED(ret, "Mutex unlock failed in worker thread");
367 }
368
369 /* Increment the operation counter */
370 c->opcnt++;
371 }
372 while (do_it);
373
374 /* Wait for the signal thread to terminate before we can exit */
375 waitsigend(c);
376 return NULL;
377 }
378
379 /***** The next function initializes a cell_t object
380 * This includes running the threads */
cell_init(int id,cell_t * c,pthread_mutexattr_t * pma)381 static void cell_init(int id, cell_t * c, pthread_mutexattr_t * pma)
382 {
383 int ret, i;
384 pthread_attr_t pa; /* We will specify a minimal stack size */
385
386 /* mark this group with its ID */
387 c->id = id;
388 /* Initialize some other values */
389 c->sigok = 0;
390 c->ctrl = 0;
391 c->sigcnt = 0;
392 c->opcnt = 0;
393 c->tcnt = 0;
394
395 /* Initialize the mutex */
396 ret = pthread_mutex_init(&(c->tmtx), NULL);
397 if (ret != 0) {
398 UNRESOLVED(ret, "Mutex init failed");
399 }
400 ret = pthread_mutex_init(&(c->mtx), pma);
401 if (ret != 0) {
402 UNRESOLVED(ret, "Mutex init failed");
403 }
404 #if VERBOSE > 1
405 output("Mutex initialized in cell %i\n", id);
406 #endif
407
408 /* Initialize the semaphore */
409 ret = sem_init(&(c->semsig), 0, 0);
410 if (ret != 0) {
411 UNRESOLVED(errno, "Sem init failed");
412 }
413 #if VERBOSE > 1
414 output("Semaphore initialized in cell %i\n", id);
415 #endif
416
417 /* Create the thread attribute with the minimal size */
418 ret = pthread_attr_init(&pa);
419 if (ret != 0) {
420 UNRESOLVED(ret, "Unable to create pthread attribute object");
421 }
422 ret = pthread_attr_setstacksize(&pa, sysconf(_SC_THREAD_STACK_MIN));
423 if (ret != 0) {
424 UNRESOLVED(ret, "Unable to specify minimal stack size");
425 }
426
427 /* Create the signal thread */
428 ret = pthread_create(&(c->threads[0]), &pa, sigthr, (void *)c);
429 if (ret != 0) {
430 UNRESOLVED(ret, "Unable to create the signal thread");
431 }
432
433 /* Create 5 "lock" threads */
434 for (i = 1; i <= 5; i++) {
435 ret = pthread_create(&(c->threads[i]), &pa, lockthr, (void *)c);
436 if (ret != 0) {
437 UNRESOLVED(ret, "Unable to create a locker thread");
438 }
439 }
440
441 /* Create 2 "timedlock" threads */
442 for (i = 6; i <= 7; i++) {
443 ret =
444 pthread_create(&(c->threads[i]), &pa, timedlockthr,
445 (void *)c);
446 if (ret != 0) {
447 UNRESOLVED(ret,
448 "Unable to create a (timed) locker thread");
449 }
450 }
451
452 /* Create 2 "trylock" threads */
453 for (i = 8; i <= 9; i++) {
454 ret =
455 pthread_create(&(c->threads[i]), &pa, trylockthr,
456 (void *)c);
457 if (ret != 0) {
458 UNRESOLVED(ret,
459 "Unable to create a (try) locker thread");
460 }
461 }
462
463 #if VERBOSE > 1
464 output("All threads initialized in cell %i\n", id);
465 #endif
466
467 /* Destroy the thread attribute object */
468 ret = pthread_attr_destroy(&pa);
469 if (ret != 0) {
470 UNRESOLVED(ret, "Unable to destroy thread attribute object");
471 }
472
473 /* Tell the signal thread to start working */
474 ret = sem_post(&(c->semsig));
475 if (ret != 0) {
476 UNRESOLVED(ret, "Unable to post signal semaphore");
477 }
478 }
479
480 /***** The next function destroys a cell_t object
481 * This includes stopping the threads */
cell_fini(int id,cell_t * c,unsigned long long * globalopcount,unsigned long long * globalsigcount)482 static void cell_fini(int id,
483 cell_t * c,
484 unsigned long long *globalopcount,
485 unsigned long long *globalsigcount)
486 {
487 int ret, i;
488
489 /* Just a basic check */
490 if (id != c->id) {
491 output("Something is wrong: Cell %i has id %i\n", id, c->id);
492 FAILED("Some memory has been corrupted");
493 }
494
495 /* Start with joining the threads */
496 for (i = 0; i < 10; i++) {
497 ret = pthread_join(c->threads[i], NULL);
498 if (ret != 0) {
499 UNRESOLVED(ret, "Unable to join a thread");
500 }
501 }
502
503 /* Destroy the semaphore and the mutex */
504 ret = sem_destroy(&(c->semsig));
505 if (ret != 0) {
506 UNRESOLVED(errno, "Unable to destroy the semaphore");
507 }
508
509 ret = pthread_mutex_destroy(&(c->mtx));
510 if (ret != 0) {
511 output("Unable to destroy the mutex in cell %i (ret = %i)\n",
512 id, ret);
513 FAILED("Mutex destruction failed");
514 }
515
516 /* Report the cell counters */
517 *globalopcount += c->opcnt;
518 *globalsigcount += c->sigcnt;
519 #if VERBOSE > 1
520 output
521 ("Counters for cell %i:\n\t%llu locks and unlocks\n\t%llu signals\n",
522 id, c->opcnt, c->sigcnt);
523 #endif
524
525 /* We are done with this cell. */
526 }
527
528 /**** Next function is called when the process is killed with SIGUSR1
529 * It tells every threads in every cells to stop their work.
530 */
globalsig(int sig)531 static void globalsig(int sig)
532 {
533 output("Signal received, processing. Please wait...\n");
534 do {
535 do_it = 0;
536 }
537 while (do_it);
538 }
539
540 /******
541 * Last but not least, the main function
542 */
main(int argc,char * argv[])543 int main(int argc, char *argv[])
544 {
545 /* Main is responsible for :
546 * the mutex attributes initializing
547 * the creation of the cells
548 * the destruction of everything on SIGUSR1 reception
549 */
550
551 int ret;
552 int i;
553 struct sigaction sa;
554 unsigned long long globopcnt = 0, globsigcnt = 0;
555
556 #ifndef WITHOUT_XOPEN
557 int sz = 2 + (sizeof(types) / sizeof(int));
558 #else
559 int sz = 2;
560 #endif
561 pthread_mutexattr_t ma[sz - 1];
562 pthread_mutexattr_t *pma[sz];
563
564 cell_t data[sz * N * SCALABILITY_FACTOR];
565
566 pma[sz - 1] = NULL;
567
568 #if VERBOSE > 0
569 output("Mutex lock / unlock stress sample is starting\n");
570 output("Kill with SIGUSR1 to stop the process\n");
571 output("\t kill -USR1 <pid>\n\n");
572 #endif
573
574 /* Initialize the mutex attributes */
575 for (i = 0; i < sz - 1; i++) {
576 pma[i] = &ma[i];
577 ret = pthread_mutexattr_init(pma[i]);
578 if (ret != 0) {
579 UNRESOLVED(ret,
580 "Unable to init a mutex attribute object");
581 }
582 #ifndef WITHOUT_XOPEN /* we have the mutex attribute types */
583 if (i != 0) {
584 ret = pthread_mutexattr_settype(pma[i], types[i - 1]);
585 if (ret != 0) {
586 UNRESOLVED(ret,
587 "Unable to set type of a mutex attribute object");
588 }
589 }
590 #endif
591 }
592 #if VERBOSE > 1
593 output("%i mutex attribute objects were initialized\n", sz - 1);
594 #endif
595
596 /* Initialize the thread-local-data key */
597 ret = pthread_key_create(&_c, NULL);
598 if (ret != 0) {
599 UNRESOLVED(ret, "Unable to initialize TLD key");
600 }
601 #if VERBOSE > 1
602 output("TLD key initialized\n");
603 #endif
604
605 /* Register the signal handler for SIGUSR1 */
606 sigemptyset(&sa.sa_mask);
607 sa.sa_flags = 0;
608 sa.sa_handler = globalsig;
609 if ((ret = sigaction(SIGUSR1, &sa, NULL))) {
610 UNRESOLVED(ret, "Unable to register signal handler");
611 }
612
613 /* Register the signal handler for SIGUSR2 */
614 sa.sa_handler = sighdl;
615 if ((ret = sigaction(SIGUSR2, &sa, NULL))) {
616 UNRESOLVED(ret, "Unable to register signal handler");
617 }
618
619 /* Start every threads */
620 #if VERBOSE > 0
621 output("%i cells of 10 threads are being created...\n",
622 sz * N * SCALABILITY_FACTOR);
623 #endif
624 for (i = 0; i < sz * N * SCALABILITY_FACTOR; i++)
625 cell_init(i, &data[i], pma[i % sz]);
626 #if VERBOSE > 0
627 output("All threads created and running.\n");
628 #endif
629
630 /* We stay here while not interrupted */
631 do {
632 sched_yield();
633 }
634 while (do_it);
635
636 #if VERBOSE > 0
637 output("Starting to join the threads...\n");
638 #endif
639 /* Everybody is stopping, we must join them, and destroy the cell data */
640 for (i = 0; i < sz * N * SCALABILITY_FACTOR; i++)
641 cell_fini(i, &data[i], &globopcnt, &globsigcnt);
642
643 /* Destroy the mutex attributes objects */
644 for (i = 0; i < sz - 1; i++) {
645 ret = pthread_mutexattr_destroy(pma[i]);
646 if (ret != 0) {
647 UNRESOLVED(ret,
648 "Unable to destroy a mutex attribute object");
649 }
650 }
651
652 /* Destroy the thread-local-data key */
653 ret = pthread_key_delete(_c);
654 if (ret != 0) {
655 UNRESOLVED(ret, "Unable to destroy TLD key");
656 }
657 #if VERBOSE > 1
658 output("TLD key destroyed\n");
659 #endif
660
661 /* output the total counters */
662 #if VERBOSE > 1
663 output("===============================================\n");
664 #endif
665 #if VERBOSE > 0
666 output("Total counters:\n\t%llu locks and unlocks\n\t%llu signals\n",
667 globopcnt, globsigcnt);
668 output("pthread_mutex_lock stress test passed.\n");
669 #endif
670
671 PASSED;
672 }
673