/*$cb
 *    This file is a part of OpenMHP VDR plugin.
 *
 *    Copyright (C) 2005 Mikko Novaro <miknov@gmail.com>
 *
 *    See README file for licensing and contact information.
 $ce*/

#include "semaphore.h"

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "macros.h"

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
  int val;                  /* value for SETVAL */
  struct semid_ds *buf;     /* buffer for IPC_STAT, IPC_SET */
  unsigned short *array;    /* array for GETALL, SETALL */
  /* Linux specific part: */
  struct seminfo *__buf;    /* buffer for IPC_INFO */
};
#endif


struct sembuf cSemaphore::sembufP = {0, -1, 0};
struct sembuf cSemaphore::sembufP_nowait = {0, -1, IPC_NOWAIT};
struct sembuf cSemaphore::sembufV = {0,  1, 0};

cSemaphore::cSemaphore(key_t semKey) {
  key = semKey;
  union semun arg;

  /* create a semaphore set with 1 semaphore: */
  if ((id = semget(key, 1, 0600 | IPC_CREAT)) == -1)
    ERR("creating semaphore failed");

  /* initialize semaphore #0 to 1: */
  arg.val = 1;
  if (semctl(id, 0, SETVAL, arg) == -1)
    ERR("initializing semaphore failed");
}

cSemaphore::~cSemaphore(void) {
  union semun arg;

  /* remove the actual semaphore: */
  if (semctl(id, 0, IPC_RMID, arg) == -1)
    ERR("removing semaphore failed");

}


bool cSemaphore::Enter(bool wait) {
  if (wait) { if (semop(id, &sembufP, 1) == -1) ERR("semaphore: entering critical section failed"); }
  else {
    if (semop(id, &sembufP_nowait, 1) == -1) {
      if (errno != EAGAIN) ERR("semaphore: entering critical section failed (with NOWAIT)");
      return false;
    }
  }
  return true;
}

void cSemaphore::Leave(void) {
  if (semop(id, &sembufV, 1) == -1) ERR("semaphore: leaving critical section failed");
}

int cSemaphore::GetValue(void) {
  return semctl(id, 0, GETVAL);
}
