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