// Illustration of shared memory, parent/child processes, and a mutex semaphore
//
// To compile this program, the "real-time" library must be linked, as follows:
//
//     g++ -lrt -o race-with-semaphores race-with-semaphores.cpp

// UNRESOLVED:  This worked on Linux, but -lrt fails on OS X...
// ???

#include <string>
#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>
#include <iostream>

using namespace std;

int main() {

  const int N = 500000;  // Number of desired increments/decrements

  typedef struct {
    long value;      // a shared counter
    sem_t mutex;     // to be used as a binary semaphore
  } 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);

  // Initialize the shared counter value
  shared_memory->value = 0;

  // Initialize the mutex semaphore
  sem_init(&shared_memory->mutex, 1, 1); // 1st argument:  the semaphore
                                         // 2nd argument: 1 ==> sempahore is shared among processes 
                                         //                     (not threads)
					 // 3rd argument:  initial value for the semaphore
  // Create a child process
  pid_t pid = fork();
  if (pid == 0) {  // code for the CHILD to execute
    // The child process increments the shared memory value
    for (int i = 0; i < N; i++) {
      sem_wait(&shared_memory->mutex);   // sem_wait(): like P(), down(), or wait()
      shared_memory->value++;
      sem_post(&shared_memory->mutex);   // sem_post(): like V(), up(), or signal()
    }
    // Let the child terminate with a successful return code
    exit(0);
  }
  else { // code for the PARENT to execute
    // Whereas the parent process decrements the shared memory value
    for (int i = 0; i < N; i++){
      sem_wait(&shared_memory->mutex);
      shared_memory->value--;
      sem_post(&shared_memory->mutex);
    }

    // Now patiently wait for the child process to terminate
    cout << "waiting for child " << pid << endl;
    int status;
    pid_t pidWaitedFor = waitpid(-1, &status, 0);

    cout << "parent has finished waiting for " << pidWaitedFor << endl;
    cout << shared_memory->value << endl;

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

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