#include<errno.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

//semaphore header file
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

#define SEMPERM 0600
#define TRUE 1
#define FALSE 0

//
union semun{
  int val;
  struct semid_ds*buf;
  unsigned short *array;
};
union semun arg;

//
struct databuf{
  int in;
  int out;
  char buffer[5];
}


//system V header file
#include<sys/shm.h>
//#include<sys/ipc.h>
//#include<signal.h>
//#include<time.h>

#define N 5
//#define ERR ((struct databuf*)-1)

static int shmid,semid;

void initshm(struct databuf**p){
  if((shmid=shmget(0x12,sizeof(struct databuf*),0600|IPC_CREAT|IPC_EXCL))==-1) 
     printf("shmget");
  if((*p=(struct databuf*)shmat(shmid,0,0))==((struct databuf*)-1))
     printf("shmid=%d",shmid);
}

//
int initsem(key_t semkey){
  int status=0,semid;
  if((semid=semget(semkey,3,SEMPERM|IPC_CREAT|IPC_EXCL))==-1)
  {
     if(errno==EEXIST)  printf("semid eexist");
  }
  else
  {
     printf("semid=%d",semid);
     arg.val=0;
     if((status=semctl(semid,0,SETVAL,arg))==-1)
        perror("semctl setval full error!");
     arg.val=N;
     if((status=semctl(semid,1,SETVAL,arg))==-1)
        perror("semctl setval empty error!");
     arg.val=1;
     if((status=semctl(semid,2,SETVAL,arg))==-1)
        perror("semctl setval mutex error!");
     if(semid==-1||status==-1)
     {
        perror("initsem failed");
        return(-1);
     }
  }
  return(semid);
}

//
void remobj(){
  if(shmctl(shmid,IPC_RMID,NULL)==-1)    printf("shmctl");
  if(semctl(semid,0,IPC_RMID,0)==-1)
  {
      if(errno==EIDRM)
        perror("semctl remobj error EIDRM!");
  }
}

//
int P(int semid,int sem_n){
 // printf("sem_n=%d",sem_n);
  struct sembuf p_buf;
     p_buf.sem_num=sem_n;
     p_buf.sem_op=-1;
     p_buf.sem_flg=SEM_UNDO;
  if(semop(semid,&p_buf,1)==-1)
  {
      perror("P(semid) failed");
      exit(1);
  }
  return(0);
}

//
int V(int semid,int sem_n)
{
  struct sembuf V_buf;
    V_buf.sem_num=sem_n;
    V_buf.sem_op=1;
    V_buf.sem_flg=SEM_UNDO;
  if(semop(semid,&V_buf,1)==-1)
  {
     perror("V(semid) failed");
     exit(1);
  }
  return(0);
}

//
void producer(int semid,struct databuf*buf){
  int in,i;
  printf("\n pok\n");

 /* char c;
  c=getchar();
  while(c!='q')
     in=buf->in;
  sleep(10);*/
  for(i=0;i<8;i++)
  {
     printf("\n producer produce a product x\n");
     P(semid,1);
     P(semid,2);
     buf->buffer[buf->in]='x';
     printf("\n pid=%d put into buffer[%d]\n",getpid(),buf->in);
     buf->in=(buf->in+1)%N;
     V(semid,0);
     v(semid,2);
     //c=getchar();
  }
}

//
void consumer(int semid,struct databuf*buf){
   int out,i;
   printf("\n cok\n");
   /*char c;
   out=buf->out;*/
   for(i=0;i<8;i++)
   {
       P(semid,0);
       P(semid,2);
       buf->buffer[buf->out]='x';
       printf("\n pid=%d get x out from buffer[%d]\n",getpid(),buf->out);
       buf->out=(buf->out+1)%N;
       V(semid,1);
       V(semid,2);
   }
}


main(){
  key_t semkey=0x12;
  int i,pid;
  struct databuf*buf;
  initahm(&buf);
  semid=initsem(semkey);
  printf("\n ok \n");
  if((pid=fork())<0)  printf("fork error!");
  else if(pid==0)
       {
          fork();
          producer(semid,buf);
       }
       else
       {
          fork();
          consumer(semid,buf);
          waitpid(pid,NULL,0);
          remobj();
       }
}