/***************************************************************************
 * Copyright 1995, Technion, Israel Institute of Technology
 * Electrical Eng, Software Lab.
 * Author:    Michael Veksler.
 ***************************************************************************
 * File:      shm_semaph.c
 * Purpose:   Handle semaphores for shared memory operations.
 ***************************************************************************
 */
#ifdef CONFIG_IPC

#define inline __inline__
#include <assert.h>
#include <unistd.h>
#include <sys/sem.h>
#include <stdio.h>
#include <errno.h>
#include <stddebug.h>
#include <debug.h>
#include "shm_semaph.h"
#define SEM_READ  0
#define SEM_WRITE 1

/* IMPORTANT: Make sure that killed process will not lock everything.
 *            If possible, restrict usage of these functions.
 */
void shm_read_wait(shm_sem semid)
{
  struct sembuf sop[2];
  int ret;
  
  dprintf_sem(stddeb,"shm_read_wait(%d)\n",semid);
  sop[0].sem_num=SEM_READ;
  sop[0].sem_op=1;		   /* add this read instance */
  sop[0].sem_flg=SEM_UNDO;	   /* undo in case process dies */
  
  sop[1].sem_num=SEM_WRITE;
  sop[1].sem_op=0;		   /* wait until no writing instance exists */
  sop[1].sem_flg=SEM_UNDO;

  do {
     ret=semop (semid,sop , 2);
  } while (ret<0 && errno==EINTR);  /* interrupted system call? */
  
  if (ret<0) 
     fprintf(stderr,"failed semaphore lock for read. semid=%d,errno=%d\n",
	     semid, errno);
}
void shm_write_wait(shm_sem semid)
{
  struct sembuf sop[3];
  int ret;
  
  dprintf_sem(stddeb,"shm_write_wait(%d)\n",semid);
  sop[0].sem_num=SEM_READ;
  sop[0].sem_op=0;		   /* wait until no reading instance exist */
  sop[0].sem_flg=SEM_UNDO;		   

  sop[1].sem_num=SEM_WRITE;
  sop[1].sem_op=1;		   /* writing is in progress - disable read */
  sop[1].sem_flg=SEM_UNDO;	   /* undo if process dies */

  sop[2].sem_num=SEM_READ;
  sop[2].sem_op=1;		   /* disable new writes */
  sop[2].sem_flg=SEM_UNDO;
  
  do {
     ret=semop (semid,sop , 3);
  } while (ret<0 && errno==EINTR);  /* interrupted system call? */

  if (ret<0) 			   /* test for the error */
     fprintf(stderr,"failed semaphore lock for write. semid=%d,errno=%d\n",
	     semid, errno);
}
void shm_write_signal(shm_sem semid)
{
  struct sembuf sop[2];
  int ret;

  dprintf_sem(stddeb,"shm_write_signal(%d)\n",semid);
  sop[0].sem_num=SEM_READ;
  sop[0].sem_op=-1;	
  sop[0].sem_flg=IPC_NOWAIT | SEM_UNDO;	/* no reason to wait */

  sop[1].sem_num=SEM_WRITE;
  sop[1].sem_op=-1;		   
  sop[1].sem_flg=IPC_NOWAIT | SEM_UNDO;	/* no reason to wait */

  do {
     ret=semop (semid,sop , 2);
  } while (ret<0 && errno==EINTR);  /* interrupted system call? */

  if (ret<0) 			   /* test for the error */
     fprintf(stderr,"failed semaphore unlock for write. semid=%d,errno=%d\n",
	     semid, errno);
}

void shm_read_signal(shm_sem semid)
{
  struct sembuf sop[2];
  int ret;

  dprintf_sem(stddeb,"shm_read_signal(%d)\n",semid);
  sop[0].sem_num=SEM_READ;
  sop[0].sem_op=-1;	
  sop[0].sem_flg=IPC_NOWAIT | SEM_UNDO;	/* no reason to wait */

  do {
     ret=semop (semid,sop , 1);
  } while (ret<0 && errno==EINTR);  /* interrupted system call? */

  if (ret<0) 			   /* test for the error */
     fprintf(stderr,"failed semaphore unlock for read. semid=%d,errno=%d\n",
	     semid, errno);
}

void shm_sem_init(shm_sem *sptr)
{
  shm_sem semid;
  union semun arg;

  semid=semget (IPC_PRIVATE, 2, 0700 | IPC_CREAT);

  arg.val=0;
  semctl (semid, 0, SETVAL, arg);
  semctl (semid, 1, SETVAL, arg);
  *sptr=semid;
}

void shm_sem_done(shm_sem *semptr)
{
  union semun arg;

  semctl (*semptr, 0, IPC_RMID , arg);
  semctl (*semptr, 1, IPC_RMID , arg);

  *semptr= -1;
}

#endif  /* CONFIG_IPC */
