/* A simple bounded-buffer program using a shared buffer and spinlocks  
 * Written by Henry Walker, 2004
 * Last modified by Janet Davis, 26 September 2006
 */

#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>          
#include <stdio.h>
#include <sys/mman.h>

#define BUF_SIZE 5     /* logical size of buffer */
#define SHARED_MEM_SIZE (BUF_SIZE+2)*sizeof(int) 
                       /* size of shared memory in bytes */
#define RUN_LENGTH 10  /* number of iterations in test run */

/* Parent process: writes values to buffer in shared memory. */
void producer( int* buffer, int* in, int* out ) {
  int i;
  for ( i = 0; i < RUN_LENGTH; i++ ) {
    while ((*in + 1) % BUF_SIZE == *out); /* busy-wait for space to write into */
    buffer[*in] = i*i;                    /* put data in buffer */
    *in = (*in + 1) % BUF_SIZE;
    printf ("Writer's report: item %2d put in buffer\n", i);
  }
}

/* Child process: reads values from buffer in shared memory. */
void consumer( int* buffer, int* in, int* out ) {
  int i;       /* child counter variable */
  int value;   /* value read by child */

  for (i = 0; i < RUN_LENGTH; i++) {
    while (*in == *out) ; /* busy-wait until there is something to read */
    value = buffer[*out];   /* get data from buffer */
    *out = (*out + 1) % BUF_SIZE;
    printf ("Reader's report: item %2d == %2d\n", i, value);
  }
}

int main (void) {
  pid_t pid;               /* variable to record process id of child */
  caddr_t shared_memory;   /* shared memory base address */

  int *in;         /* pointer to logical 'in' address for producer */
  int *out;        /* pointer to logical 'out' address for consumer */
  int *buffer;     /* logical base address for buffer */
 
  /* set up shared memory segment */
  shared_memory = mmap( 0, SHARED_MEM_SIZE, 
                        PROT_READ | PROT_WRITE, 
                        MAP_ANONYMOUS | MAP_SHARED, 
                        -1, 0 );
  if ( shared_memory == (caddr_t)-1 ) { 
    perror( "error in mmap while allocating shared memory\n" );
    exit( 1 );
  }

   /* shared memory segment will be organized as follows:
    *  0                                                n-1  n  n+1
    * --------------------------------------------------------------
    * |                                                    |   |   |
    * --------------------------------------------------------------
    *  ^                                                     ^   ^
    *  buffer                                                in  out     
    */

   buffer = (int*) shared_memory; 
            /* logical buffer starts at beginning of shared segment */
   in  = (int*) shared_memory + BUF_SIZE * sizeof(int);
   out = (int*) shared_memory + (BUF_SIZE+1) * sizeof(int);

   *in = *out = 0;          /* initial starting index */

   /* spawn child process and check for error */
   if ( -1 == (pid = fork()) )  {
     perror ("error in fork");  
     exit (1);
   }

   if ( 0 == pid ) {
     /* processing for child == consumer */
     printf ("The consumer process begins.\n");
     consumer( buffer, in, out );
     printf ("Reader done.\n");

   } else { 
     /* processing for parent == producer */
     printf ("The producer process begins.\n");
     producer( buffer, in, out );
     wait( NULL );
     printf ("Writer done.\n");  
   }
   exit (0);
}
