/* A program in which two processes increment a global counter stored in a 
 * file. Demonstrates the use of System V semaphores.
 * To compile: gcc -o trade trade.c
 */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>

#define SEMNAME "tradesem"

int acquire(int);
int release(int id);
int incrCounter(void);

int main()
{
    FILE *fp;
    int counter, i, status;
    int pid;
    int id, key;
    int retval; /* Return value from semop() */

    /* The semun struct must be declared in your program */
    union semun {
	int val;
	struct semid_ds *buf;
	ushort *array;
    } argument;
    argument.val = 1;

    /* Create the semaphore.  Give permissions to the world */
    if((key = ftok(SEMNAME, 'k')) < 0) {
	perror("ftok");
	exit(1);
    }

    id = semget(key, 1, 0666 | IPC_CREAT);
    if(id < 0) {
	fprintf(stderr, "Unable to obtain semaphore\n");
    }

    /* Note that only one process initializes the semaphore */
    if( semctl(id, 0, SETVAL, argument) < 0)  {
	fprintf( stderr, "Cannot set semaphore value.\n");
    } else {
	fprintf(stderr, "Semaphore %d initialized.\n", key);
    }

    /* Initialize the share variable. Note that we still only have one
       process, so there is no concurrency problem. */

    if((fp = fopen("/tmp/sharedval", "w")) == NULL) {
	fprintf(stderr, "Could not open /tmp/sharedval\n");
	exit(1);
    }

    fprintf(fp, "10\n");
    fprintf(stdout, "Initialize shared value to 10\n");
    fclose(fp);
    pid = fork();

    if(pid == 0) {

	for(i = 0; i < 10; i++) {
	    /* We only want to proceed if acquire succeeded */
	    if((acquire(id)) == 0) {
		counter = incrCounter();
		if( i == 5) sleep(1);
		fprintf(stdout, "Child incremented counter to %d\n", counter);
		if((release(id)) != 0) {
		    exit(1);
		}
	    }
	}

    } else {

	for(i = 0; i < 10; i++) {
	    if((acquire(id)) == 0) {
		counter = incrCounter();
		if( i == 3) sleep(1);
		fprintf(stdout, "Parent incremented counter to %d\n", counter);
		if( release(id) != 0) {
		    break;
		}

	    }
	}

	wait(&status);

	/* Remove the semaphore */
	if((semctl(id, 0, IPC_RMID)) < 0) {
	    perror("semctl");
	} else {
	    printf("Removed semaphore\n");
	}

	/* Remove the file */
	if((unlink("/tmp/sharedval")) < 0 ) {
	    perror("Remove File");
	    exit(1);
	}

    }

}

/* Increment a counter variable stored in a file. */
/* If someone else has a file called /tmp/sharedval on the machine, this
 * code may not work.
 */
int incrCounter() {
    FILE *fp;
    char buf[256];
    int counter;

    if((fp = fopen("/tmp/sharedval", "r+")) == NULL) {
	fprintf(stderr, "Could not open /tmp/sharedval\n");
	exit(1);
    }
    if(fgets(buf, 256, fp) == NULL){
	perror("Couldn't read from file");
    }
    counter = atoi(buf);
    rewind(fp);
    fprintf(fp, "%d\n", counter+1);
    fclose(fp);
    return (counter+1);

}


int acquire(int id)
{
    int retval;
    struct sembuf operations[1];
    /* Set up the sembuf structure. */
    operations[0].sem_num = 0;  /* index into semaphore array */
    operations[0].sem_op = -1;  
    operations[0].sem_flg = 0;  /* wait if semaphore is <=0 */

    retval = semop(id, operations, 1);

    if(retval == 0) {
	printf("Successful acquire by process %d\n", getpid());
    }
    else {
	printf("acquire operation did not succeed\n");
        perror("REASON");
    }
    return retval;
}

int release(int id)
{
    int retval;
    struct sembuf operations[1];
   /* Set up the sembuf structure. */
    operations[0].sem_num = 0;
    operations[0].sem_op = 1;
    operations[0].sem_flg = 0;

    retval = semop(id, operations, 1);

    if(retval == 0)  {
	printf("Successful release by process %d.\n", getpid());
    } else {
	printf("Release did not succeed.\n");
        perror("REASON");
    }

    return retval;
}
