// Illustration of shared memory, parent/child processes, and a mutex semaphore
//

#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
    } 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;

    pid_t pid = fork();
    if (pid == 0) {  // code for the CHILD to execute

	sem_t * mutex;
	mutex = sem_open("mysem", O_CREAT, 0600, 1);
      
	// The child process increments the shared memory value
	for (int 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()
	}
	// Let the child terminate with a successful return code
	exit(0);
    }
    else { // code for the PARENT to execute
	sem_t * mutex;
	mutex = sem_open("mysem", O_CREAT, 0600, 1);
	
	// Whereas the parent process decrements the shared memory value
	for (int i = 0; i < N; i++){
	    sem_wait(mutex);
	    shared_memory->value--;
	    sem_post(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;
}
