/*
 * share-sem.c
 * 
 * a simple semaphore example using Sys V IPC type segments
 * Consumer creates a semaphore and blocks 
 * Producer attaches to the semaphore and signals
 *
 * Mark Claypool, 2002, WPI
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>		/* for IPC */
#include <sys/sem.h>		/* semaphore stuff */
#include <errno.h>

/* note: semun may be defined on some systems. */
union semun {
  int val;                    /* value for SETVAL */
  struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
  unsigned short int *array;  /* array for GETALL, SETALL */
  struct seminfo *__buf;      /* buffer for IPC_INFO */
};

#define KEY 3013		/* identification key to get segment */
#define PERMS 0666		/* -rw-rw-rw- */
#define MEMSIZE 80		/* bytes to allocate */

int main(int argc, char *argv[]) {
  union semun arg;		/* used to set semaphore parameters */
  struct sembuf sembuf;		/* used to do a wait and signal */
  int sem_id;			/* the id, like a file descriptor */
  int flags;			/* flags for semget() call */

  /* check command line args */
  if ((argc != 2) || (argv[1][0] != 'p' && argv[1][0] != 'c')) {
    fprintf(stderr, "usage: share-sem {p|c}, where:\n");
    fprintf(stderr, "       p is for the producer (start second)\n");
    fprintf(stderr, "       c is for the consumer (start first)\n");
    exit(1);
  } 

  /***************************************/

  /* Warning!  Below code does not verify that consumer started first */
  flags = PERMS|IPC_CREAT;

  /* system calls to create the semaphore */
  sem_id = semget(KEY, 1, flags);  /* create 1 semaphore in set (index 0) */
  if (sem_id < 0) {
    perror("Note, consumer must be started first");
    exit(1);
  } 

  if (argv[1][0] != 'p') {

    /* initialize the semaphore */
    arg.val = 0;		/* initalize semphore to 0 */
    if (semctl(sem_id, 0, SETVAL, arg) < 0) {
      perror ("initialize failed");
      exit(1);
    }
  }

  /***************************************/

  if (argv[1][0] == 'p') {

    /* Producer does a semsignal() */
    printf("Producer signaling....\n");
    sembuf.sem_num = 0;		/* first sem in set */
    sembuf.sem_op = 1;		/* 1 means signal */
    sembuf.sem_flg = 0;
    if (semop(sem_id, &sembuf, 1) == -1) {
      perror("signal failed");
      exit(1);
    }

  } else {

    /* Consumer does a semwait() */
    printf("Consumer waiting ....\n");
    sembuf.sem_num = 0;		/* first sem in set */
    sembuf.sem_op = -1;		/* -1 means wait */
    sembuf.sem_flg = 0;
    if (semop(sem_id, &sembuf, 1) == -1) {
      perror("wait failed");
      exit(1);
    }

    printf("Producer has signaled.\n");

    /* delete */
    if (semctl(sem_id, 0, IPC_RMID, arg) == -1) {
      perror("unsuccessful delete");
      exit(1);
    }
  }

  printf("Done.\n");
  exit(0);
  
}

