#include "ace/SV_Semaphore_Simple.h"
#include "ace/Log_Category.h"
#include "ace/ACE.h"
#include "ace/os_include/sys/os_sem.h"
#if defined (ACE_HAS_ALLOC_HOOKS)
# include "ace/Malloc_Base.h"
#endif /* ACE_HAS_ALLOC_HOOKS */

#if !defined (__ACE_INLINE__)
#include "ace/SV_Semaphore_Simple.inl"
#endif /* !__ACE_INLINE__ */

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_ALLOC_HOOK_DEFINE (ACE_SV_Semaphore_Simple)

void
ACE_SV_Semaphore_Simple::dump () const
{
#if defined (ACE_HAS_DUMP)
  ACE_TRACE ("ACE_SV_Semaphore_Simple::dump");
#endif /* ACE_HAS_DUMP */
}

int
ACE_SV_Semaphore_Simple::control (int cmd,
                                  int value,
                                  u_short semnum) const
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::control");
  if (this->internal_id_ == -1)
    return -1;
  else
    {
      semun semctl_arg;

      semctl_arg.val = value;
      return ACE_OS::semctl (this->internal_id_,
                             semnum,
                             cmd,
                             semctl_arg);
    }
}

#ifdef ACE_HAS_SYSV_IPC
int
ACE_SV_Semaphore_Simple::init (key_t k, int i)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::init");
  this->key_ = k;
  this->internal_id_ = i;
  return 0;
}
#endif

// General ACE_SV_Semaphore operation. Increment or decrement by a
// specific amount (positive or negative; amount can`t be zero).

int
ACE_SV_Semaphore_Simple::op (short val, u_short n, short flags) const
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::op");
  sembuf op_op;

  op_op.sem_num = n;
  op_op.sem_flg = flags;

  if (this->internal_id_ == -1)
    return -1;
  else if ((op_op.sem_op = val) == 0)
    return -1;
  else
    return ACE_OS::semop (this->internal_id_, &op_op, 1);
}

// Open or create one or more SV_Semaphores.  We return 0 if all is
// OK, else -1.

int
ACE_SV_Semaphore_Simple::open (key_t k,
                               short flags,
                               int initial_value,
                               u_short n,
                               mode_t perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::open");
  union semun ivalue;

#ifdef ACE_HAS_SYSV_IPC
  if (k == IPC_PRIVATE || k == static_cast<key_t> (ACE_INVALID_SEM_KEY))
    return -1;
#endif

  ivalue.val = initial_value;
  this->key_ = k;
  this->sem_number_ = n;

  this->internal_id_ = ACE_OS::semget (this->key_, n, perms | flags);

  if (this->internal_id_ == -1)
    return -1;

  if (ACE_BIT_ENABLED (flags, IPC_CREAT))
    for (int i = 0; i < n; i++)
      if (ACE_OS::semctl (this->internal_id_, i, SETVAL, ivalue) == -1)
        return -1;

  return 0;
}

ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple (key_t k,
                                                  short flags,
                                                  int initial_value,
                                                  u_short n,
                                                  mode_t perms)
  : key_ (k)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple");
  if (this->open (k, flags, initial_value, n, perms) == -1)
    ACELIB_ERROR ((LM_ERROR,  ACE_TEXT ("%p\n"),  ACE_TEXT ("ACE_SV_Semaphore::ACE_SV_Semaphore")));
}

// Convert name to key.  This function is used internally to create keys
// for the semaphores.
//
// The method for generating names is a 32 bit CRC, but still we
// measured close to collition ratio of nearly 0.1% for
// ACE::unique_name()-like strings.

key_t
ACE_SV_Semaphore_Simple::name_2_key (const char *name)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::name_2_key");

  if (name == 0)
    {
      errno = EINVAL;
#ifdef ACE_HAS_SYSV_IPC
      return static_cast<key_t> (ACE_INVALID_SEM_KEY);
#else
      key_t ret = ACE_DEFAULT_SEM_KEY;
      return ret;
#endif
    }

  // Basically "hash" the values in the <name>.  This won't
  // necessarily guarantee uniqueness of all keys.
  // But (IMHO) CRC32 is good enough for most purposes (Carlos)
#if defined (ACE_WIN32) && defined (_MSC_VER)
  // The cast below is legit...
#  pragma warning(push)
#  pragma warning(disable : 4312)
#endif /* defined (ACE_WIN32) && defined (_MSC_VER) */

#ifdef ACE_HAS_SYSV_IPC
  return (key_t) ACE::crc32 (name);
#else
  key_t ret = ACE_DEFAULT_SEM_KEY;
  return ret;
#endif

#if defined (ACE_WIN32) && defined (_MSC_VER)
#  pragma warning(pop)
#endif /* defined (ACE_WIN32) && defined (_MSC_VER) */
}

// Open or create a ACE_SV_Semaphore.  We return 1 if all is OK, else
// 0.

int
ACE_SV_Semaphore_Simple::open (const char *name,
                               short flags,
                               int initial_value,
                               u_short n,
                               mode_t perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::open");

  key_t key = ACE_DEFAULT_SEM_KEY;

#ifdef ACE_HAS_SYSV_IPC
  if (name != 0)
    key = this->name_2_key (name);
#else
  ACE_UNUSED_ARG (name);
#endif

  return this->open (key, flags, initial_value, n, perms);
}

ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple (const char *name,
                                                  short flags,
                                                  int initial_value,
                                                  u_short n,
                                                  mode_t perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple");
  if (this->open (name,
                  flags,
                  initial_value,
                  n,
                  perms) == -1)
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("%p\n"),
                ACE_TEXT ("ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple")));
}

#if defined (ACE_HAS_WCHAR)
ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple (const wchar_t *name,
                                                  short flags,
                                                  int initial_value,
                                                  u_short nsems,
                                                  mode_t perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple(wchar_t)");
  if (this->open (ACE_Wide_To_Ascii (name).char_rep (),
                  flags,
                  initial_value,
                  nsems,
                  perms) == -1)
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("%p\n"),
                ACE_TEXT ("ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple")));
}
#endif /* ACE_HAS_WCHAR */

ACE_SV_Semaphore_Simple::~ACE_SV_Semaphore_Simple ()
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::~ACE_SV_Semaphore_Simple");
  this->close ();
}

ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple () :
  sem_number_ (0)
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::ACE_SV_Semaphore_Simple");
#ifdef ACE_HAS_SYSV_IPC
  this->init ();
#endif
}

// Remove all SV_Semaphores associated with a particular key.  This
// call is intended to be called from a server, for example, when it
// is being shut down, as we do an IPC_RMID on the ACE_SV_Semaphore,
// regardless of whether other processes may be using it or not.  Most
// other processes should use close() below.

int
ACE_SV_Semaphore_Simple::remove () const
{
  ACE_TRACE ("ACE_SV_Semaphore_Simple::remove");
  int const result = this->control (IPC_RMID);
#ifdef ACE_HAS_SYSV_IPC
  ((ACE_SV_Semaphore_Simple *) this)->init ();
#endif
  return result;
}

ACE_END_VERSIONED_NAMESPACE_DECL
