/*
  Aaron Linville
  aaron@linville.org
  http://www.linville.org
 
  Synchronized producer and consumers with semaphores.
*/

#define SHMPERM 0600 // octal
#define SEMPERM 0600 // octal

#include <iostream.h>
#include <sys/errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int p(int); // Wait
int v(int); // Signal

union {
  int val;
  struct semid_ds *buffer;
  ushort *array;
} eenum;

union {
  int val;
  struct semid_ds *buffer;
  ushort *array;
} snum;

union {
  int val;
  struct semid_ds *buffer;
  ushort *array;
} nnum;

int main(int, char*[]) {  
  int pid, semids, semidn, semide, shmid, stat_loc;
  char *shm, *s;
  
  // Keys
  key_t key = IPC_PRIVATE;
  
  // Args to be passed to semget()
  int nsems = 1;
  int semflg = IPC_CREAT | IPC_EXCL | SEMPERM;
  
  // Args to be passed to shmget()
  int size = 10;
  int shmflg = IPC_CREAT | IPC_EXCL | SHMPERM;
  
  // Get semaphore s, n, and e
  if(((semids = semget(key, nsems, semflg)) < 0) ||
     ((semidn = semget(key, nsems, semflg)) < 0) ||
     ((semide = semget(key, nsems, semflg)) < 0)) {
    cerr << "Unable to get one of the semaphores! Errno: "
         << errno << endl;
    return 0;
  }
  
  snum.val = 1;
  nnum.val = 0;
  eenum.val = 10;
  
  if((semctl(semids, 0, SETVAL, snum) == -1) ||
     (semctl(semidn, 0, SETVAL, nnum) == -1) ||
     (semctl(semide, 0, SETVAL, eenum) == -1)) {
    cerr << "Unable to set the value on a semaphore! Errno: "
         << errno << endl;
    return 0;
  }
  
  // Get shared memory
  if((shmid = shmget(key, size, shmflg)) < 0) {
    cerr << "Unable to get shared memory! Errno: "
         << errno << endl;
    return 0;
  }
  
  if((pid = fork()) < 0) {
    cerr << "Unable to fork! Errno: "
         << errno << endl;
    return 0;
  } else {
    if(pid == 0) { // Consumer
      if((shm = (char *) shmat(shmid, NULL, 0)) == (void *) -1) {
        cerr << "Consumer unable to attach! Errno: "
             << errno << endl;
        return 0;
      }
      
      // Consume the first 100 items
      // 10 byte buffer means read it 10 times
      for(int i = 0; i < 10; i++) {
        s = shm;
        for(int j = 0; j < 10; j++) {
          p(semidn);
          p(semids);
          cout << int(*s++) << endl;
          v(semids);
          v(semide);
        }
      }
      
      return 0;
    } else { // Producer
      if((shm = (char *) shmat(shmid, NULL, 0)) == (void *) -1) {
        cerr << "Producer unable to attach! Errno: "
        << errno << endl;
        return 0;
      }
      
      // Produce the first 100 numbers
      // 10 byte buffer means fill it 10 times
      for(int i = 0; i < 10; i++) {
        s = shm;
        for(int j = 0; j < 10; j++) {
          p(semide);
          p(semids);
          *s++ = (i * 10) + j; // I love pointer math.
          v(semids);
          v(semidn);
        }
      }
      
      if(wait(&stat_loc) == -1) {
        cerr << "No child to wait for! Errno: " << errno << endl;
      }
      
      if((semctl(semids, 0, IPC_RMID, NULL) == -1) ||
         (semctl(semidn, 0, IPC_RMID, NULL) == -1) ||
         (semctl(semide, 0, IPC_RMID, NULL) == -1))
        cerr << "Unable to remove semaphore! Errno: "
             << errno << endl;
      
      if(shmctl(shmid, IPC_RMID, NULL) == -1) {
        cerr << "Unable to destroy segment! Errno: "
             << errno << endl;
      }
    } 
  } 
  
  return 0;
} 

// Perform wait
int p(int semid){
  struct sembuf p_buf;
  
  p_buf.sem_num = 0;
  p_buf.sem_op = -1;
  p_buf.sem_flg = 0;
  
  if (semop(semid, &p_buf, 1) < 0) {
    cerr << "Fatal wait error on semaphore "
         << semid << endl;
    return -1;
  }
  
  return 0;
}

// Perform signal
int v(int semid) {
  struct sembuf v_buf;
  
  v_buf.sem_num = 0;
  v_buf.sem_op = 1;
  v_buf.sem_flg = 0;
  
  if(semop(semid, &v_buf, 1) < 0) {
    cerr << "Fatal signal error on semaphore "
         << semid << endl;
    return -1;
  }
  
  return 0;
}
