1 /* TODO: proper output */ 2 3 /* 4 * 5 * Copyright (c) International Business Machines Corp., 2001 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 15 * the GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22 /* 23 * FILE : pth_str01.c 24 * DESCRIPTION : create a tree of threads 25 * HISTORY: 26 * 04/09/2001 Paul Larson (plars (at) us.ibm.com) 27 * -Ported 28 * 29 */ 30 31 #include <pthread.h> 32 #include <stdio.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <stdint.h> 38 #include <sys/types.h> 39 #include "test.h" 40 #include "pth_str01.h" 41 42 int depth = 3; 43 int breadth = 4; 44 int timeout = 30; /* minutes */ 45 int cdepth; /* current depth */ 46 int debug = 0; 47 48 c_info *child_info; /* pointer to info array */ 49 int node_count; /* number of nodes created so far */ 50 pthread_mutex_t node_mutex; /* mutex for the node_count */ 51 pthread_cond_t node_condvar; /* condition variable for node_count */ 52 pthread_attr_t attr; /* attributes for created threads */ 53 54 int num_nodes(int, int); 55 int synchronize_children(c_info *); 56 int doit(c_info *); 57 58 char *TCID = "pth_str01"; 59 int TST_TOTAL = 1; 60 61 void testexit(int value) 62 { 63 if (value == 0) 64 tst_resm(TPASS, "Test passed"); 65 else 66 tst_resm(TFAIL, "Test failed"); 67 68 exit(value); 69 } 70 71 /* 72 * parse_args 73 * 74 * Parse command line arguments. Any errors cause the program to exit 75 * at this point. 76 */ 77 static void parse_args(int argc, char *argv[]) 78 { 79 int opt, errflag = 0; 80 int bflag = 0, dflag = 0, tflag = 0; 81 82 while ((opt = getopt(argc, argv, "b:d:t:Dh?")) != EOF) { 83 switch (opt) { 84 case 'b': 85 if (bflag) 86 errflag++; 87 else { 88 bflag++; 89 breadth = atoi(optarg); 90 if (breadth <= 0) 91 errflag++; 92 } 93 break; 94 case 'd': 95 if (dflag) 96 errflag++; 97 else { 98 dflag++; 99 depth = atoi(optarg); 100 if (depth <= 0) 101 errflag++; 102 } 103 break; 104 case 't': 105 if (tflag) 106 errflag++; 107 else { 108 tflag++; 109 timeout = atoi(optarg); 110 if (timeout <= 0) 111 errflag++; 112 } 113 break; 114 case 'D': 115 debug = 1; 116 break; 117 case 'h': 118 default: 119 errflag++; 120 break; 121 } 122 } 123 124 if (errflag) { 125 fprintf(stderr, 126 "usage: %s [-b <num>] [-d <num>] [-t <num>] [-D]", 127 argv[0]); 128 fprintf(stderr, " where:\n"); 129 fprintf(stderr, "\t-b <num>\tbreadth of child nodes\n"); 130 fprintf(stderr, "\t-d <num>\tdepth of child nodes\n"); 131 fprintf(stderr, 132 "\t-t <num>\ttimeout for child communication (in minutes)\n"); 133 fprintf(stderr, "\t-D\t\tdebug mode on\n"); 134 testexit(1); 135 } 136 137 } 138 139 /* 140 * num_nodes 141 * 142 * Caculate the number of child nodes for a given breadth and depth tree. 143 */ 144 int num_nodes(int b, int d) 145 { 146 int n, sum = 1, partial_exp = 1; 147 148 /* 149 * The total number of children = sum ( b ** n ) 150 * n=0->d 151 * Since b ** 0 == 1, and it's hard to compute that kind of value 152 * in this simplistic loop, we start sum at 1 (above) to compensate 153 * and do the computations from 1->d below. 154 */ 155 for (n = 1; n <= d; n++) { 156 partial_exp *= b; 157 sum += partial_exp; 158 } 159 160 return (sum); 161 } 162 163 /* 164 * synchronize_children 165 * 166 * Register the child with the parent and then wait for all of the children 167 * at the same level to register also. Return when everything is synched up. 168 */ 169 int synchronize_children(c_info * parent) 170 { 171 int my_index, rc; 172 c_info *info_p; 173 struct timespec timer; 174 175 if (debug) { 176 printf("trying to lock node_mutex\n"); 177 fflush(stdout); 178 } 179 180 /* 181 * Lock the node_count mutex to we can safely increment it. We 182 * will unlock it when we broadcast that all of our siblings have 183 * been created or when we block waiting for that broadcast. 184 */ 185 pthread_mutex_lock(&node_mutex); 186 my_index = node_count++; 187 188 tst_resm(TINFO, "thread %d started", my_index); 189 190 /* 191 * Get a pointer into the array of thread structures which will 192 * be "me". 193 */ 194 info_p = &child_info[my_index]; 195 info_p->index = my_index; 196 197 if (debug) { 198 printf("thread %d info_p=%p\n", my_index, info_p); 199 fflush(stdout); 200 } 201 202 /* 203 * Register with parent bumping the parent's child_count variable. 204 * Make sure we have exclusive access to that variable before we 205 * do the increment. 206 */ 207 if (debug) { 208 printf("thread %d locking child_mutex %p\n", my_index, 209 &parent->child_mutex); 210 fflush(stdout); 211 } 212 pthread_mutex_lock(&parent->child_mutex); 213 if (debug) { 214 printf("thread %d bumping child_count (currently %d)\n", 215 my_index, parent->child_count); 216 fflush(stdout); 217 } 218 parent->child_ptrs[parent->child_count++] = info_p; 219 if (debug) { 220 printf("thread %d unlocking child_mutex %p\n", my_index, 221 &parent->child_mutex); 222 fflush(stdout); 223 } 224 pthread_mutex_unlock(&parent->child_mutex); 225 226 if (debug) { 227 printf("thread %d node_count = %d\n", my_index, node_count); 228 printf("expecting %d nodes\n", num_nodes(breadth, cdepth)); 229 fflush(stdout); 230 } 231 232 /* 233 * Have all the nodes at our level in the tree been created yet? 234 * If so, then send out a broadcast to wake everyone else up (to let 235 * them know they can now create their children (if they are not 236 * leaf nodes)). Otherwise, go to sleep waiting for the broadcast. 237 */ 238 if (node_count == num_nodes(breadth, cdepth)) { 239 240 /* 241 * Increase the current depth variable, as the tree is now 242 * fully one level taller. 243 */ 244 if (debug) { 245 printf("thread %d doing cdepth++ (%d)\n", my_index, 246 cdepth); 247 fflush(stdout); 248 } 249 cdepth++; 250 251 if (debug) { 252 printf("thread %d sending child_mutex broadcast\n", 253 my_index); 254 fflush(stdout); 255 } 256 /* 257 * Since all of our siblings have been created, this level of 258 * the tree is now allowed to continue its work, so wake up the 259 * rest of the siblings. 260 */ 261 pthread_cond_broadcast(&node_condvar); 262 263 } else { 264 265 /* 266 * In this case, not all of our siblings at this level of the 267 * tree have been created, so go to sleep and wait for the 268 * broadcast on node_condvar. 269 */ 270 if (debug) { 271 printf("thread %d waiting for siblings to register\n", 272 my_index); 273 fflush(stdout); 274 } 275 time(&timer.tv_sec); 276 timer.tv_sec += (unsigned long)timeout *60; 277 timer.tv_nsec = (unsigned long)0; 278 if ((rc = pthread_cond_timedwait(&node_condvar, &node_mutex, 279 &timer))) { 280 tst_resm(TINFO, 281 "pthread_cond_timedwait (sync) %d: %s\n", 282 my_index, strerror(rc)); 283 testexit(2); 284 } 285 286 if (debug) { 287 printf("thread %d is now unblocked\n", my_index); 288 fflush(stdout); 289 } 290 291 } 292 293 /* 294 * Unlock the node_mutex lock, as this thread is finished 295 * initializing. 296 */ 297 if (debug) { 298 printf("thread %d unlocking node_mutex\n", my_index); 299 fflush(stdout); 300 } 301 pthread_mutex_unlock(&node_mutex); 302 if (debug) { 303 printf("thread %d unlocked node_mutex\n", my_index); 304 fflush(stdout); 305 } 306 307 if (debug) { 308 printf("synchronize_children returning %d\n", my_index); 309 fflush(stdout); 310 } 311 312 return (my_index); 313 314 } 315 316 /* 317 * doit 318 * 319 * Do it. 320 */ 321 int doit(c_info * parent) 322 { 323 int rc, child, my_index; 324 void *status; 325 c_info *info_p; 326 struct timespec timer; 327 328 if (parent != NULL) { 329 /* 330 * Synchronize with our siblings so that all the children at 331 * a given level have been created before we allow those children 332 * to spawn new ones (or do anything else for that matter). 333 */ 334 if (debug) { 335 printf("non-root child calling synchronize_children\n"); 336 fflush(stdout); 337 } 338 my_index = synchronize_children(parent); 339 if (debug) { 340 printf("non-root child has been assigned index %d\n", 341 my_index); 342 fflush(stdout); 343 } 344 } else { 345 /* 346 * The first thread has no one with which to synchronize, so 347 * set some initial values for things. 348 */ 349 if (debug) { 350 printf("root child\n"); 351 fflush(stdout); 352 } 353 cdepth = 1; 354 my_index = 0; 355 node_count = 1; 356 } 357 358 /* 359 * Figure out our place in the pthread array. 360 */ 361 info_p = &child_info[my_index]; 362 363 if (debug) { 364 printf("thread %d getting to heart of doit.\n", my_index); 365 printf("info_p=%p, cdepth=%d, depth=%d\n", info_p, cdepth, 366 depth); 367 fflush(stdout); 368 } 369 370 if (cdepth <= depth) { 371 372 /* 373 * Since the tree is not yet complete (it is not yet tall enough), 374 * we need to create another level of children. 375 */ 376 377 tst_resm(TINFO, "thread %d creating kids, cdepth=%d", my_index, 378 cdepth); 379 380 /* 381 * Create breadth children. 382 */ 383 for (child = 0; child < breadth; child++) { 384 if (debug) { 385 printf("thread %d making child %d, ptr=%p\n", 386 my_index, child, 387 &(info_p->threads[child])); 388 fflush(stdout); 389 } 390 if ((rc = 391 pthread_create(&(info_p->threads[child]), &attr, 392 (void *)doit, (void *)info_p))) { 393 tst_resm(TINFO, "pthread_create (doit): %s\n", 394 strerror(rc)); 395 testexit(3); 396 } else { 397 if (debug) { 398 printf 399 ("pthread_create made thread %p\n", 400 &(info_p->threads[child])); 401 fflush(stdout); 402 } 403 } 404 } 405 406 if (debug) { 407 printf("thread %d waits on kids, cdepth=%d\n", my_index, 408 cdepth); 409 fflush(stdout); 410 } 411 412 /* 413 * Wait for our children to finish before we exit ourselves. 414 */ 415 for (child = 0; child < breadth; child++) { 416 if (debug) { 417 printf("attempting join on thread %p\n", 418 &(info_p->threads[child])); 419 fflush(stdout); 420 } 421 if ((rc = 422 pthread_join((info_p->threads[child]), &status))) { 423 if (debug) { 424 printf 425 ("join failed on thread %d, status addr=%p: %s\n", 426 my_index, status, strerror(rc)); 427 fflush(stdout); 428 } 429 testexit(4); 430 } else { 431 if (debug) { 432 printf("thread %d joined child %d ok\n", 433 my_index, child); 434 fflush(stdout); 435 } 436 } 437 } 438 439 } else { 440 441 /* 442 * This is the leaf node case. These children don't create 443 * any kids; they just talk amongst themselves. 444 */ 445 tst_resm(TINFO, "thread %d is a leaf node, depth=%d", my_index, 446 cdepth); 447 448 /* 449 * Talk to siblings (children of the same parent node). 450 */ 451 if (breadth > 1) { 452 453 for (child = 0; child < breadth; child++) { 454 /* 455 * Don't talk to yourself. 456 */ 457 if (parent->child_ptrs[child] != info_p) { 458 if (debug) { 459 printf 460 ("thread %d locking talk_mutex\n", 461 my_index); 462 fflush(stdout); 463 } 464 pthread_mutex_lock(& 465 (parent-> 466 child_ptrs[child]-> 467 talk_mutex)); 468 if (++parent->child_ptrs[child]-> 469 talk_count == (breadth - 1)) { 470 if (debug) { 471 printf 472 ("thread %d talk siblings\n", 473 my_index); 474 fflush(stdout); 475 } 476 if ((rc = 477 pthread_cond_broadcast 478 (&parent-> 479 child_ptrs[child]-> 480 talk_condvar))) { 481 tst_resm(TINFO, 482 "pthread_cond_broadcast: %s\n", 483 strerror(rc)); 484 testexit(5); 485 } 486 } 487 if (debug) { 488 printf 489 ("thread %d unlocking talk_mutex\n", 490 my_index); 491 fflush(stdout); 492 } 493 pthread_mutex_unlock(& 494 (parent-> 495 child_ptrs 496 [child]-> 497 talk_mutex)); 498 } 499 } 500 501 /* 502 * Wait until the breadth - 1 siblings have contacted us. 503 */ 504 if (debug) { 505 printf("thread %d waiting for talk siblings\n", 506 my_index); 507 fflush(stdout); 508 } 509 510 pthread_mutex_lock(&info_p->talk_mutex); 511 if (info_p->talk_count < (breadth - 1)) { 512 time(&timer.tv_sec); 513 timer.tv_sec += (unsigned long)timeout *60; 514 timer.tv_nsec = (unsigned long)0; 515 if ((rc = 516 pthread_cond_timedwait(&info_p-> 517 talk_condvar, 518 &info_p->talk_mutex, 519 &timer))) { 520 tst_resm(TINFO, 521 "pthread_cond_timedwait (leaf) %d: %s\n", 522 my_index, strerror(rc)); 523 testexit(6); 524 } 525 } 526 pthread_mutex_unlock(&info_p->talk_mutex); 527 528 } 529 530 } 531 532 /* 533 * Our work is done. We're outta here. 534 */ 535 tst_resm(TINFO, "thread %d exiting, depth=%d, status=%d, addr=%p", 536 my_index, cdepth, info_p->status, info_p); 537 538 pthread_exit(0); 539 540 } 541 542 /* 543 * main 544 */ 545 int main(int argc, char *argv[]) 546 { 547 int rc, ind, total; 548 pthread_t root_thread; 549 550 parse_args(argc, argv); 551 552 /* 553 * Initialize node mutex. 554 */ 555 if ((rc = pthread_mutex_init(&node_mutex, NULL))) { 556 tst_resm(TINFO, "pthread_mutex_init(node_mutex): %s\n", 557 strerror(rc)); 558 testexit(7); 559 } 560 561 /* 562 * Initialize node condition variable. 563 */ 564 if ((rc = pthread_cond_init(&node_condvar, NULL))) { 565 tst_resm(TINFO, "pthread_cond_init(node_condvar): %s\n", 566 strerror(rc)); 567 testexit(8); 568 } 569 570 /* 571 * Allocate pthread info structure array. 572 */ 573 total = num_nodes(breadth, depth); 574 tst_resm(TINFO, "Allocating %d nodes.", total); 575 if ((child_info = malloc(total * sizeof(c_info))) 576 == NULL) { 577 perror("malloc child_info"); 578 testexit(10); 579 } 580 memset(child_info, 0x00, total * sizeof(c_info)); 581 582 if (debug) { 583 printf("Initializing array for %d children\n", total); 584 fflush(stdout); 585 } 586 587 /* 588 * Allocate array of pthreads descriptors and initialize variables. 589 */ 590 for (ind = 0; ind < total; ind++) { 591 592 if ((child_info[ind].threads = malloc(breadth * sizeof(pthread_t))) 593 == NULL) { 594 perror("malloc threads"); 595 testexit(11); 596 } 597 memset(child_info[ind].threads, 0x00, 598 breadth * sizeof(pthread_t)); 599 600 if ((child_info[ind].child_ptrs = malloc(breadth * sizeof(c_info *))) == NULL) { 601 perror("malloc child_ptrs"); 602 testexit(12); 603 } 604 memset(child_info[ind].child_ptrs, 0x00, 605 breadth * sizeof(c_info *)); 606 607 if ((rc = pthread_mutex_init(&child_info[ind].child_mutex, 608 NULL))) { 609 tst_resm(TINFO, "pthread_mutex_init child_mutex: %s\n", 610 strerror(rc)); 611 testexit(13); 612 } 613 614 if ((rc = pthread_mutex_init(&child_info[ind].talk_mutex, 615 NULL))) { 616 tst_resm(TINFO, "pthread_mutex_init talk_mutex: %s\n", 617 strerror(rc)); 618 testexit(14); 619 } 620 621 if ((rc = pthread_cond_init(&child_info[ind].child_condvar, 622 NULL))) { 623 tst_resm(TINFO, 624 "pthread_cond_init child_condvar: %s\n", 625 strerror(rc)); 626 testexit(15); 627 } 628 629 if ((rc = pthread_cond_init(&child_info[ind].talk_condvar, 630 NULL))) { 631 tst_resm(TINFO, "pthread_cond_init talk_condvar: %s\n", 632 strerror(rc)); 633 testexit(16); 634 } 635 636 if (debug) { 637 printf("Successfully initialized child %d.\n", ind); 638 fflush(stdout); 639 } 640 641 } 642 643 tst_resm(TINFO, 644 "Creating root thread attributes via pthread_attr_init."); 645 646 if ((rc = pthread_attr_init(&attr))) { 647 tst_resm(TINFO, "pthread_attr_init: %s\n", strerror(rc)); 648 testexit(17); 649 } 650 651 /* 652 * Make sure that all the thread children we create have the 653 * PTHREAD_CREATE_JOINABLE attribute. If they don't, then the 654 * pthread_join call will sometimes fail and cause mass confusion. 655 */ 656 if ((rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) 657 ) { 658 tst_resm(TINFO, "pthread_attr_setdetachstate: %s\n", 659 strerror(rc)); 660 testexit(18); 661 } 662 663 tst_resm(TINFO, "Creating root thread via pthread_create."); 664 665 if ((rc = pthread_create(&root_thread, &attr, (void *)doit, NULL))) { 666 tst_resm(TINFO, "pthread_create: %s\n", strerror(rc)); 667 testexit(19); 668 } 669 670 if (debug) { 671 printf("Doing pthread_join.\n"); 672 fflush(stdout); 673 } 674 675 /* 676 * Wait for the root child to exit. 677 */ 678 if ((rc = pthread_join(root_thread, NULL))) { 679 tst_resm(TINFO, "pthread_join: %s\n", strerror(rc)); 680 testexit(20); 681 } 682 683 if (debug) { 684 printf("About to pthread_exit.\n"); 685 fflush(stdout); 686 } 687 688 testexit(0); 689 690 exit(0); 691 } 692