/*
  MAT 4970
  Bill Slough
  
  Illustration of shared memory, parent/child processes,
  and a mutex semaphore.

  Adjust the value of N to experimentally determine if the mutex
  is protecting the update operations on the shared memory value.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <semaphore.h>

#define N 1000000   /* How many increments/decrements? */

int main() {

    typedef struct {
	long value;      
    } shared_memory_t;

    /* An identifier for the shared memory segment is needed */
    int segment_id;

    /* We also will need a pointer to the shared memory segment */
    shared_memory_t * shared_memory;

    /* How many bytes of shared memory are needed? */
    const int size = sizeof(shared_memory_t);

    /* Allocate a shared memory segment */
    segment_id = shmget(IPC_PRIVATE, size, S_IRUSR | S_IWUSR);

    /* Attach the shared memory segment */
    shared_memory = (shared_memory_t *) shmat(segment_id, NULL, 0);

    shared_memory->value = 0;

    /* loop counter */
    int i;

    printf("\nChild will increment; parent will decrement...\n\n");
   
    /* Create a child process */
    pid_t pid = fork();
    
    if (pid == 0) {  /* code for the CHILD to execute */
	sem_t * mutex;
	mutex = sem_open("mysem", O_CREAT, 0600, 1);
	if (mutex == SEM_FAILED) {
	    perror("child sem_open");
	    exit(1);
	}
      
	/* The child process simply increments the shared memory value... */
	for (i = 0; i < N; i++) {
	    sem_wait(mutex);          /* sem_wait(): like P(), down(), or wait() */
	    shared_memory->value++;
	    sem_post(mutex);          /* sem_post(): like V(), up(), or signal() */
	}
    }
    else { /* code for the PARENT to execute */
	sem_t * mutex;
	mutex = sem_open("mysem", O_CREAT, 0600, 1);
	if (mutex == SEM_FAILED) {
	    perror("parent sem_open");
	    exit(1);
	}
	
	/* Whereas the parent process decrements the shared memory value */
	for (i = 0; i < N; i++) {
	    sem_wait(mutex);
	    shared_memory->value--;
	    sem_post(mutex);
	}

	/* Now wait for the child process to finish */
	printf("The parent is now waiting for child: %d\n", pid);
	wait(NULL);

	printf("OK; child has terminated...\n");
	printf("final value = %ld\n\n", shared_memory->value);

	/* Detach the shared memory segment */ 
	if (shmdt(shared_memory) == -1) {
	    fprintf(stderr, "Unable to detach\n");
	}

	/* Remove the shared memory segment */
	shmctl(segment_id, IPC_RMID, NULL); 	
    }
    return 0;
}
