/* Filename: p3.c
 * Created on Tuesday, Feb. 5, 2002
 *
 * In this program, two processes are accessing the same shared memory segment
 * without mutual exclusion.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define MAX_TRANS   2000000L

struct account_struct {
  long cnt;
  double amount;
};

int main() {
  int   myshmid;              /* shared mem identifier */
  key_t mykey;                /* key  */
  struct account_struct *p_acc;   /* pointer to my shared data */

  /* Before creating shared memory segment, a key value must be prepared. */
  mykey = ftok(".", 'C');  

  /* Then, a shared memory segment can be created using that key value. 
   * If the shmget call is successful, it will return a non-negative integer.
   * Otherwise, -1 will be returned. 
   */ 
  printf("\nshmget: Creating a shared memory segment using a key 0x%x and a mode 0666\n", mykey);
  myshmid = shmget(mykey, sizeof(struct account_struct), IPC_CREAT | 0666);
  if (myshmid == -1 ) {
    perror("shmget: error");
    exit(1);  
  }
  printf("shmget: Success. identifier for my shared mem is %d\n\n", myshmid);

  /* Attach the shared memory segment to this process's data segment. */
  printf("\nshmat: Attaching shared mem segment\n");
  if ((p_acc = (struct account_struct *) shmat(myshmid, 0, 0)) == (struct account_struct *) -1) {
    perror("shmat: Failed to attach.");
    exit(2);
  }
  printf("shmat: Successfully attached\n");

  /* Initialize the account. */
  p_acc->cnt = 0L;
  p_acc->amount = 0.0;

  fflush(stdout); /* This is necessary right before calling fork(). */

  switch(fork()) {
  case -1: perror("fork: error");break;
  case  0: 
           { /* Child process adds 1 to the account MAX_TRANS times. */
             long i;

             for (i = 0L; i < MAX_TRANS; i++) { 
                 p_acc->amount += 1.0;
                 p_acc->cnt++;
             }
             
             shmdt((void *)p_acc);
             exit(0);
           } 
  default:          
           { /* Parent process subtracts the account by 1 MAX_TRANS times. */
             long i;

             for (i = 0L; i < MAX_TRANS; i++) { 
                 p_acc->amount -= 1.0;
                 p_acc->cnt++;
             }
             wait(NULL);

             printf("\naccount.cnt    = %10d\n", p_acc->cnt);
             printf("account.amount = %13.2f\n", p_acc->amount);
             shmdt((void *)p_acc);
           } 
  }

  /* Remove a shared mem segment. */
  printf("\nshmctl: Removing a shared mem segment.  identifier = %d\n", myshmid);
  if (shmctl(myshmid, IPC_RMID, 0) == -1) {
    perror("shmctl: Failed to remove shared mem segment.");
    exit(4);
  }
  printf("shmctl: Successfully removed shared mem segment.\n\n");
  printf("\n");
  return 0;
}

