1 /* 2 * Copyright (C) Bull S.A. 1996 3 * Copyright (c) International Business Machines Corp., 2001 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 /*---------------------------------------------------------------------+ 20 | shmem_test_07.c | 21 | ==================================================================== | 22 | | 23 | Description: Verify shared memory functions with | 24 | | 25 | Algorithm: | 26 | ## shared memory segments ## | 27 | * from 1 up to number_of_writer | 28 | { | 29 | o Obtain three shared memory segments per writer | 30 | one for storing the read count (number of thread | 31 | reading "scratch" shm) , | 32 | another for storing the checksums of readers , | 33 | and the last for the "scratch" shared memory segment| 34 | for storing a series of values . | 35 | } | 36 | ## Threads ## | 37 | * from 1 up to number_of_writer | 38 | { | 39 | Initializes mutexes , | 40 | Insure the writer gets first access to the segment: | 41 | Threads are synchronized with Condition Varaiables | 42 | | 43 | thread_hold[number_of_writer]=1; | 44 | | 45 | } | 46 | * from 1 up to number_of_writer | 47 | { | 48 | Create/Start all num_writers threads (writer) | 49 | from 1 up to number_of_reader | 50 | { | 51 | Create/Start all num_readers threads (reader) | 52 | } | 53 | } | 54 | * from 1 up to number_of_writer | 55 | { | 56 | Wait for the termination of writer thread | 57 | from 1 up to number_of_reader | 58 | { | 59 | Wait for the termination of reader thread | 60 | } | 61 | } | 62 | * from 1 up to number_of_writer | 63 | { | 64 | Get writer checksum | 65 | from 1 up to number_of_reader | 66 | { | 67 | Verify that each checksum calculated by readers| 68 | match with the writer checksum | 69 | } | 70 | } | 71 |----------------------------------------------------------------------| 72 | writer () | 73 |----------------------------------------------------------------------| 74 | o Writer: | 75 | - Fill the "scratch" shared memory segment up | 76 | with data | 77 | - Compute the segment checksum | 78 | - release lock (reader threads may begin) i.e: | 79 | | 80 | thread_hold[num_w]=0; | 81 | | 82 |----------------------------------------------------------------------| 83 | reader () | 84 |----------------------------------------------------------------------| 85 | o Reader: | 86 | - Check to see if we need to wait i.e: | 87 | | 88 | while (thread_hold[num_w]) wait; | 89 | | 90 | - Evaluate checksum | 91 | - Store the resulting checksum | 92 |----------------------------------------------------------------------| 93 | | 94 | | 95 | System calls: The following system calls are tested: | 96 | shmget () | 97 | shmat () | 98 | shmctl () | 99 | | 100 | The following system calls are used: | 101 | malloc () | 102 | pthread_mutex_init() | 103 | pthread_cond_init() | 104 | pthread_attr_init() | 105 | pthread_attr_setdetachstate() | 106 | pthread_create() | 107 | pthread_join() | 108 | pthread_mutex_lock() | 109 | pthread_cond_broadcast() | 110 | pthread_mutex_unlock() | 111 | pthread_cond_wait() | 112 | pthread_mutex_destroy() | 113 | | 114 | | 115 | Usage: | 116 | shmem_test_07 [-c num_readers] [-s shmem_size] [-g num_writers]| 117 | | 118 | To compile: | 119 | cc_r -g -lpthreads -lc_r -o shmem_test_07 shmem_test_07.c | 120 | | 121 | Last update: | 122 | | 123 | Change Activity | 124 | | 125 | Version Date Name Reason | 126 | 0.1 011697 JM Initial version for AIX 4.2G | 127 | | 128 +---------------------------------------------------------------------*/ 129 #define _XOPEN_SOURCE 600 130 #include <pthread.h> 131 #include <errno.h> 132 #include <stdio.h> 133 #include <string.h> 134 #include <unistd.h> 135 #include <limits.h> 136 #include <sys/ipc.h> 137 #include <sys/shm.h> 138 #include <sys/sem.h> 139 #include <sys/signal.h> 140 #include <sys/types.h> 141 #include <sys/wait.h> 142 #include <stdlib.h> 143 144 #include <sys/stat.h> 145 /* Defines 146 * 147 * DEFAULT_SHMEM_SIZE: default shared memory size, unless specified with 148 * -s command line option 149 * 150 * SHMEM_MODE: shared memory access permissions (permit process to read 151 * and write access) 152 * 153 * USAGE: usage statement 154 */ 155 #define MAX_THREAD_NUMBER 500 156 #define MAX_WRITER_NUMBER 100 157 #define MAX_READER_NUMBER 400 158 159 #define DEFAULT_NUM_READERS 2 160 #define DEFAULT_NUM_WRITERS 2 161 162 #define SHMEM_MODE (SHM_R | SHM_W) 163 164 #define DEFAULT_SHMEM_SIZE 200000 165 #define MB (1024*1024) 166 #define MAX_SHMEM_NUMBER 11 167 168 #define USAGE "\nUsage: %s [-c num_readers] [-g num_writers] [-s shmem_size]\n\n" \ 169 "\t-c num_readers number of thread (readers) to create\n" \ 170 "\t-g num_writers number of thread (writers) to create\n" \ 171 "\t-s buffer_size size of shared memory segment (bytes)\n" \ 172 "\t (must be less than 256MB!)\n\n" 173 174 /* 175 * Function prototypes 176 * 177 * parse_args (): Parse command line arguments 178 * reader (): Thread program 179 * writer (): "scratch" each and every shared memory segment 180 * sys_error (): System error message function 181 * error (): Error message function 182 * release (): Release the shared memory segments 183 */ 184 static void parse_args(int, char **); 185 static void *reader(void *); 186 static void *writer(void *); 187 static void sys_error(const char *, int); 188 static void error(const char *, int); 189 static void release(); 190 191 /* 192 * Global Variables: 193 * 194 * checksum: Array of checksums computed by reader threads 195 * read_count: number of reader threads reading data 196 197 * num_readers: number of reader threads to create 198 * num_writers: number of writer threads to create 199 * buffer_size: size of "scratch" shared memory segment 200 * parent_pid: Process id of the parent 201 */ 202 pthread_t *writer_th; 203 pthread_t *reader_th; 204 205 pthread_mutex_t mutex_r[MAX_WRITER_NUMBER]; 206 pthread_mutex_t cond_mutex[MAX_WRITER_NUMBER]; 207 int thread_hold[MAX_WRITER_NUMBER]; 208 pthread_cond_t cond_var[MAX_WRITER_NUMBER]; 209 210 int *read_count[MAX_WRITER_NUMBER]; /* Shared memory segment address */ 211 unsigned long *checksum[MAX_WRITER_NUMBER]; /* Shared memory segment address */ 212 unsigned char *shmptr[MAX_WRITER_NUMBER]; /* Shared memory segment address */ 213 unsigned long cksum[MAX_WRITER_NUMBER]; /* Shared memory segment checksum */ 214 215 int shmem_size = DEFAULT_SHMEM_SIZE; 216 pid_t parent_pid; /* process id of parent */ 217 218 int num_readers = DEFAULT_NUM_READERS; 219 int buffer_size = DEFAULT_SHMEM_SIZE; 220 int num_writers = DEFAULT_NUM_WRITERS; 221 222 int shmid[MAX_THREAD_NUMBER + MAX_WRITER_NUMBER]; 223 224 /*---------------------------------------------------------------------+ 225 | main | 226 | ==================================================================== | 227 | | 228 | Function: Main program (see prolog for more details) | 229 | | 230 | Returns: (0) Successful completion | 231 | (-1) Error occurred | 232 | | 233 +---------------------------------------------------------------------*/ 234 int main(int argc, char **argv) 235 { 236 pthread_attr_t newattr; 237 238 int i; /* Misc loop index */ 239 int j; /* Misc loop index */ 240 int k; /* Misc loop index */ 241 size_t Size; /* Size (in bytes) of shared memory segment */ 242 243 unsigned long *ulptr; /* Misc pointer */ 244 /* Index into shared memory segment */ 245 246 /* 247 * Parse command line arguments and print out program header 248 */ 249 parse_args(argc, argv); 250 251 printf("%s: IPC Shared Memory TestSuite program\n", *argv); 252 /* 253 * Show options in effect. 254 */ 255 printf("\tNumber of writers = %d\n", num_writers); 256 printf("\tNumber of readers = %d\n", num_readers); 257 printf("\tBytes per writer = %d\n", buffer_size); 258 259 /*---------------------------------------------------------------------+ 260 | shared memory segments | 261 +---------------------------------------------------------------------*/ 262 263 for (i = 0; i < num_writers; i++) { 264 /* 265 * Obtain a unique shared memory identifier with shmget (). 266 * Attach the shared memory segment to the process with shmat (). 267 */ 268 269 j = i * 3; 270 Size = sizeof(int); 271 /* 272 * Create a shared memory segment for storing the read count 273 * (number of reader threads reading shared data) 274 * After creating the shared memory segment, initialize it. 275 */ 276 277 if ((shmid[j] = shmget(IPC_PRIVATE, Size, SHMEM_MODE)) < 0) 278 sys_error("read_count shmget failed", __LINE__); 279 280 if ((long)(read_count[i] = shmat(shmid[j], 0, 0)) == -1) 281 sys_error("shmat failed", __LINE__); 282 283 *(read_count[i]) = 0; 284 285 /* 286 * Create a shared memory segment for storing the checksums of readers. 287 * After creating the shared memory segment, initialize it. 288 */ 289 290 j++; 291 Size = sizeof(unsigned long) * num_readers; 292 293 if ((shmid[j] = shmget(IPC_PRIVATE, Size, SHMEM_MODE)) < 0) 294 sys_error("checksum shmget failed", __LINE__); 295 296 if ((long)(checksum[i] = shmat(shmid[j], 0, 0)) 297 == -1) 298 sys_error("shmat failed", __LINE__); 299 300 ulptr = checksum[i]; 301 302 for (k = 0; k < num_readers; k++) { 303 *ulptr = 0; 304 ulptr++; 305 } 306 307 /* 308 * Create the "scratch" shared memory segment for storing 309 * a series of values . 310 */ 311 312 Size = buffer_size; 313 j++; 314 315 if ((shmid[j] = shmget(IPC_PRIVATE, Size, SHMEM_MODE)) < 0) 316 sys_error("shmptr shmget failed", __LINE__); 317 318 if ((long)(shmptr[i] = shmat(shmid[j], 0, 0)) == -1) 319 sys_error("shmat failed", __LINE__); 320 321 } 322 /*---------------------------------------------------------------------+ 323 | Threads | 324 +---------------------------------------------------------------------*/ 325 326 /* 327 * Create threads array... 328 */ 329 writer_th = malloc((size_t)(num_writers * sizeof(pthread_t))); 330 reader_th = malloc((size_t)(num_writers * num_readers * sizeof(pthread_t))); 331 /* 332 * Initializes mutexes and sets their attributes 333 */ 334 for (i = 0; i < num_writers; i++) { 335 336 if (pthread_mutex_init(&mutex_r[i], NULL) != 0) 337 sys_error("Can't initialize mutex_r", __LINE__); 338 339 if (pthread_mutex_init(&cond_mutex[i], NULL)) 340 sys_error("Can't initialize cond_mutex", __LINE__); 341 if (pthread_cond_init(&cond_var[i], NULL)) 342 sys_error("cond_init(&cond_var) failed", __LINE__); 343 /* 344 * lock the access to the shared memory data segment -- 345 * get lock now to insure the writer gets first access to the segment. 346 * 347 */ 348 349 thread_hold[i] = 1; 350 351 } 352 353 /* 354 * Creates a thread attributes object and initializes it 355 * with default values. 356 */ 357 if (pthread_attr_init(&newattr)) 358 sys_error("attr_init(&newattr) failed", __LINE__); 359 /* 360 * Sets the value of the detachstate attribute of a thread attributes 361 * object : 362 * PTHREAD_CREATE_UNDETACHED Specifies that the thread will be 363 * created in undetached state. 364 */ 365 #ifdef _LINUX_ 366 // the DEFAULT state for linux pthread_create is to be "undetatched" or joinable 367 /* if (pthread_attr_setdetachstate (&newattr, PTHREAD_CREATE_JOINABLE)) 368 sys_error ("attr_setdetachstate(&newattr) failed", __LINE__); */ 369 #else 370 if (pthread_attr_setdetachstate(&newattr, PTHREAD_CREATE_UNDETACHED)) 371 sys_error("attr_setdetachstate(&newattr) failed", __LINE__); 372 #endif 373 374 /* 375 * Create all num_writers threads . Each writer thread will fill 376 * the "scratch" shared memory segment (shmptr) up with data and 377 * will store the result in cksum array accessible by the main. 378 */ 379 380 for (i = 0; i < num_writers; i++) { 381 if (pthread_create 382 (&writer_th[i], &newattr, writer, (void *)(long)i)) 383 sys_error("writer: pthread_create failed", __LINE__); 384 385 /* 386 * Create all num_readers threads . Each reader thread will compute 387 * the checksum of the shared memory segment (shmptr) and will store 388 * the result in the other shared memory segment (checksum)accessible 389 * by the writer. 390 */ 391 392 k = i * num_readers; 393 for (j = k; j < (k + num_readers); j++) { 394 if (pthread_create 395 (&reader_th[j], &newattr, reader, (void *)(long)j)) 396 sys_error("reader: pthread_create failed", 397 __LINE__); 398 } 399 } 400 401 for (i = 0; i < num_writers; i++) { 402 if (pthread_join(writer_th[i], NULL)) { 403 printf("writer_th: pthread_join return: %d\n", i); 404 sys_error("pthread_join bad status", __LINE__); 405 } 406 407 /* 408 * Wait for the reader threads to compute the checksums and complete. 409 */ 410 k = i * num_readers; 411 for (j = k; j < (k + num_readers); j++) { 412 if (pthread_join(reader_th[j], NULL)) { 413 printf("reader_th: pthread_join return: %d\n", 414 j); 415 sys_error("pthread_join bad status", __LINE__); 416 } 417 } 418 } 419 420 /* 421 * After the threads complete, check their exit status to insure 422 * that they ran to completion and then verify the corresponding 423 * checksum. 424 */ 425 for (i = 0; i < num_writers; i++) { 426 ulptr = checksum[i]; 427 for (j = 0; j < num_readers; j++) { 428 429 if (cksum[i] != *ulptr) 430 error("checksums do not match", __LINE__); 431 432 } 433 } 434 printf("\n\tMain: readers calculated segment successfully\n"); 435 436 release(); 437 printf("\nsuccessful!\n"); 438 439 return (0); 440 } 441 442 /*---------------------------------------------------------------------+ 443 | writer () | 444 | ==================================================================== | 445 | | 446 | Function: Fill the "scratch" shared memory segment up with data and | 447 | compute the segment checksum. | 448 | Release "write" lock after completing so that the readers | 449 | are able to start. | 450 | | 451 | Updates: cksum[] - array containing checksums computed by writers.| 452 | data shared memory segment filled up. | 453 | | 454 +---------------------------------------------------------------------*/ 455 void *writer(void *parm) 456 { 457 int num_w = (int)(long)parm; 458 unsigned long cksum_w = 0; /* Shared memory regions checksum */ 459 unsigned char data = 0; /* Value written into shared memory segment */ 460 unsigned char *ptr; /* Misc pointer */ 461 462 /* 463 * Fill the "scratch" shared memory segment up with data and 464 * compute the segments checksum. Release "write" lock after 465 * completing so that the reader threads may begin to read the 466 * data. 467 */ 468 data = num_w; 469 470 for (ptr = shmptr[num_w]; ptr < (shmptr[num_w] + buffer_size); ptr++) { 471 *ptr = (data++) % (UCHAR_MAX + 1); 472 cksum_w += *ptr; 473 } 474 if (pthread_mutex_lock(&cond_mutex[num_w])) 475 sys_error("mutex_lock(&cond_mutex) failed", __LINE__); 476 thread_hold[num_w] = 0; 477 if (pthread_cond_broadcast(&cond_var[num_w])) 478 sys_error("cond_signal(&cond_var) failed", __LINE__); 479 if (pthread_mutex_unlock(&cond_mutex[num_w])) 480 sys_error("mutex_unlock(&cond_mutex) failed", __LINE__); 481 482 cksum[num_w] = cksum_w; 483 printf("\t\twriter (%03d): shared memory checksum %08lx\n", num_w, 484 cksum_w); 485 486 return NULL; 487 } 488 489 /*---------------------------------------------------------------------+ 490 | reader () | 491 | ==================================================================== | 492 | | 493 | Function: Waits for read access to the shared memory segment, | 494 | computes the shared memory segments checksum and releases | 495 | the read lock. Then stores the checksum. | 496 | | 497 | Updates: checksum - shared memory segment containing checksums | 498 | computed by reader threads | 499 | | 500 +---------------------------------------------------------------------*/ 501 void *reader(void *parm) 502 { 503 int num_p = (int)(long)parm; 504 unsigned long cksum_r = 0; /* Shared memory regions checksum */ 505 int i; /* Misc index */ 506 int num_r; /* Misc index */ 507 int num_w; /* Misc index */ 508 unsigned char *ptr; /* Misc pointer */ 509 unsigned long *ulptr_r; /* Misc pointer */ 510 511 /* 512 * Wait for a READ_COUNT lock on the shared memory segment, then 513 * compute the checksum and release the READ_COUNT lock. 514 */ 515 516 num_r = num_p % num_readers; 517 num_w = num_p - num_r; 518 num_w = num_w / num_readers; 519 ptr = shmptr[num_w]; 520 ulptr_r = checksum[num_w]; 521 522 if (pthread_mutex_lock(&cond_mutex[num_w])) 523 sys_error("Can't take cond lock", __LINE__); 524 /* 525 * Check to see if we need to wait: if yes, wait for the condition 526 * variable (blocking wait). 527 */ 528 while (thread_hold[num_w]) { 529 if (pthread_cond_wait(&cond_var[num_w], &cond_mutex[num_w])) 530 sys_error("cond_wait failed", __LINE__); 531 } 532 if (pthread_mutex_unlock(&cond_mutex[num_w])) 533 sys_error("Can't release cond lock", __LINE__); 534 535 if (pthread_mutex_lock(&mutex_r[num_w])) 536 sys_error("Can't take read lock", __LINE__); 537 538 (*(read_count[num_w]))++; 539 540 if (pthread_mutex_unlock(&mutex_r[num_w])) 541 sys_error("Can't release read lock", __LINE__); 542 543 for (i = 0; i < buffer_size; i++) 544 cksum_r += *ptr++; 545 546 if (pthread_mutex_lock(&mutex_r[num_w])) 547 sys_error("Can't take read lock", __LINE__); 548 (*(read_count[num_w]))--; 549 if (pthread_mutex_unlock(&mutex_r[num_w])) 550 sys_error("Can't release 1 read lock", __LINE__); 551 552 /* 553 * Store the resulting checksum and print out a message 554 */ 555 556 *ulptr_r = cksum_r; 557 printf("\t\treader (%03d) of writer (%03d): checksum %08lx\n", num_r, 558 num_w, cksum_r); 559 return NULL; 560 } 561 562 /*---------------------------------------------------------------------+ 563 | parse_args () | 564 | ==================================================================== | 565 | | 566 | Function: Parse the command line arguments & initialize global | 567 | variables. | 568 | | 569 | Updates: (command line options) | 570 | | 571 | [-s] size: shared memory segment size | 572 | | 573 | [-c] num_readers: number of reader threads | 574 | | 575 | [-g] num_writers: number of writer threads | 576 | | 577 +---------------------------------------------------------------------*/ 578 void parse_args(int argc, char **argv) 579 { 580 int i; 581 int errflag = 0; 582 char *program_name = *argv; 583 extern char *optarg; /* Command line option */ 584 585 while ((i = getopt(argc, argv, "c:s:g:?")) != EOF) { 586 switch (i) { 587 case 'c': 588 num_readers = atoi(optarg); 589 break; 590 case 's': 591 buffer_size = atoi(optarg); 592 break; 593 case 'g': /* number of group */ 594 num_writers = atoi(optarg); 595 break; 596 case '?': 597 errflag++; 598 break; 599 } 600 } 601 if (num_writers >= MAX_WRITER_NUMBER) { 602 errflag++; 603 fprintf(stderr, "ERROR: num_writers must be less than %d\n", 604 MAX_WRITER_NUMBER); 605 } 606 if (num_readers >= MAX_READER_NUMBER) { 607 errflag++; 608 fprintf(stderr, "ERROR: num_readers must be less than %d\n", 609 MAX_READER_NUMBER); 610 } 611 i = num_readers * num_writers; 612 if (i >= MAX_THREAD_NUMBER) { 613 errflag++; 614 fprintf(stderr, 615 "ERROR: maximun threads number must be less than %d\n", 616 MAX_THREAD_NUMBER); 617 } 618 619 if (errflag) { 620 fprintf(stderr, USAGE, program_name); 621 exit(2); 622 } 623 } 624 625 /*---------------------------------------------------------------------+ 626 | release () | 627 | ==================================================================== | 628 | | 629 | Function: Release shared memory regions. | 630 | | 631 +---------------------------------------------------------------------*/ 632 void release() 633 { 634 int i; 635 int j; 636 for (i = 0; i < num_writers; i++) { 637 if (pthread_mutex_destroy(&cond_mutex[i]) != 0) 638 sys_error("Can't destroy cond_mutex", __LINE__); 639 if (pthread_mutex_destroy(&mutex_r[i]) != 0) 640 sys_error("Can't destroy mutex_r", __LINE__); 641 } 642 643 for (i = 0; i < num_writers; i++) { 644 /* 645 * Release shared memory regions 646 */ 647 j = i * 3; 648 if (shmctl(shmid[j], IPC_RMID, 0) < 0) 649 sys_error("read_count shmctl failed", __LINE__); 650 j++; 651 if (shmctl(shmid[j], IPC_RMID, 0) < 0) 652 sys_error("checksum shmctl failed", __LINE__); 653 j++; 654 if (shmctl(shmid[j], IPC_RMID, 0) < 0) 655 sys_error("shmptr shmctl failed", __LINE__); 656 657 } 658 } 659 660 /*---------------------------------------------------------------------+ 661 | sys_error () | 662 | ==================================================================== | 663 | | 664 | Function: Creates system error message and calls error () | 665 | | 666 +---------------------------------------------------------------------*/ 667 void sys_error(const char *msg, int line) 668 { 669 char syserr_msg[256]; 670 671 sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno)); 672 error(syserr_msg, line); 673 } 674 675 /*---------------------------------------------------------------------+ 676 | error () | 677 | ==================================================================== | 678 | | 679 | Function: Prints out message and exits... | 680 | | 681 +---------------------------------------------------------------------*/ 682 void error(const char *msg, int line) 683 { 684 fprintf(stderr, "ERROR [line: %d] %s\n", line, msg); 685 if (line >= 260) 686 release(); 687 exit(-1); 688 } 689