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 +---------------------------------------------------------------------*/
main(int argc,char ** argv)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 +---------------------------------------------------------------------*/
writer(void * parm)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 +---------------------------------------------------------------------*/
reader(void * parm)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 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)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 +---------------------------------------------------------------------*/
release()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 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)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 +---------------------------------------------------------------------*/
error(const char * msg,int line)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