/*!========================================================================

  @file         RTESync_BinarySemaphore.cpp
  @ingroup      Synchronisation
  @author       StefanP

  @brief        Semaphore class for non-counting semaphores (also called binary semaphores or Events
                                                             respectively)

  @since        2003-09-29  17:47
  @sa           

  ==========================================================================

  \if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2003-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end

  \endif
 ============================================================================*/



/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/
/*
#define ASSERT_ARGUMENT_OPTION  SAPDBERR_CRASH
#define ASSERT_STATE_OPTION     SAPDBERR_CRASH
#define ASSERT_RANGE_OPTION     SAPDBERR_CRASH
*/

#if !defined(WIN32)
#include    "RunTime/RTE_saveUNIXcalls.h" /* nocheck */
#endif /* WIN32 */

#include    "RunTime/Synchronisation/RTESync_BinarySemaphore.hpp"
#include    "RunTime/RTE_GlobalNameSpaceNT.h"
#include    "RunTime/RTE_Message.hpp"
#include    "RunTime/RTE_Sync_Messages.hpp"
#include    "RunTime/RTE_Messages.hpp"
#include    "RunTime/MemoryManagement/RTEMem_RteAllocator.hpp"
#include    "SAPDBCommon/Tracing/SAPDBTrace_Topic.hpp"
#include    "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"



extern SAPDBTrace_Topic Sync_Trace;
/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/


/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/


/*===========================================================================*
 *  LOCAL CLASSES, STRUCTURES, TYPES, UNIONS ...                             *
 *===========================================================================*/



/*===========================================================================*
 *  STATIC/INLINE FUNCTION PROTOTYPES                                        *
 *===========================================================================*/



/*===========================================================================*
  *  METHODS                                                                  *
 *===========================================================================*/

RTESync_BinarySemaphore::RTESync_BinarySemaphore ()   
    : m_Handle (RTE_UNDEF_HANDLE),
#if !defined (_WIN32)
      m_UserType (UndefUser),
#endif
      m_SemID (RTE_SYNC_UNDEF_SEM_ID)
{
    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::RTESync_BinarySemaphore", Sync_Trace, 9);
}

/*---------------------------------------------------------------------------*/

RTESync_BinarySemaphore::~RTESync_BinarySemaphore ()
{
    SAPDBErr_MessageList       messageList;

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::~RTESync_BinarySemaphore", Sync_Trace, 9);

    if (NoError != Close (messageList))
    {
        messageList = messageList + SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_CLOSE);
        RTE_Message (messageList);
    }
}

/*---------------------------------------------------------------------------*/

#if defined (_WIN32)
RTESync_BinarySemaphore::SemRC    RTESync_BinarySemaphore::Create   
(
    RTESync_SemaphoreID const       pSemID,
    PSECURITY_ATTRIBUTES const      pSA,
    SAPDBErr_MessageList &          messageList
)
{
    RTE_SystemRc                    rc = RTE_SYSTEMRC_NO_ERROR;
    SAPDB_UInt4                     nameBufferLength = 0; 
    SemRC                           retCode = NoError;

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Create", Sync_Trace, 9);

    if (NULL != pSemID)
    {
        RTE_BuildGlobalNameSpaceName (pSemID, NULL, &nameBufferLength); //Determine the length needed 
                                                                                   //for the semaphore name
        m_SemID = (SAPDB_Char *)(RTEMem_RteAllocator::Instance().Allocate (nameBufferLength));
        if (NULL == m_SemID)
        {
            messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_ALLOCATE_SEMID,
                                                              SAPDB_ToString (nameBufferLength),
                                                              pSemID);
            return Error;
        }

        RTE_BuildGlobalNameSpaceName (pSemID, m_SemID, &nameBufferLength);
    }


    /* Automatic Reset; Initial state: nonsignaled */
    m_Handle = CreateEvent (pSA, FALSE, FALSE, m_SemID);
  
    if (NULL != m_Handle)
    {
        rc  = GetLastError ();
        if (rc == RTE_SYSTEMRC_ALREADY_EXISTS)
        {
            messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_EXIST,
                NULL == m_SemID ? "UNNAMED" : m_SemID);
            retCode = AlreadyExist;
        }
    }
    else
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_CREATE,
            NULL == m_SemID ? "UNNAMED" : m_SemID, 
            SAPDB_ToString (GetLastError ()));
        retCode = Error;
    }
    
    return retCode;
}

/*---------------------------------------------------------------------------*/

RTESync_BinarySemaphore::SemRC    RTESync_BinarySemaphore::Open   
(
    RTESync_SemaphoreID const       semID,
    SAPDBErr_MessageList &          messageList
)
{
    RTE_SystemRc            rc = RTE_SYSTEMRC_NO_ERROR;
    SAPDB_UInt4             nameBufferLength = 0; 
    SemRC                   retCode = NoError;

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Open", Sync_Trace, 9);

    if (RTE_UNDEF_HANDLE == m_Handle)
    {
        if (NULL != semID)
        {
            RTE_BuildGlobalNameSpaceName (semID, NULL, &nameBufferLength); //Determine the length needed 
            //for the semaphore name
            m_SemID = (SAPDB_Char *)(RTEMem_RteAllocator::Instance().Allocate (nameBufferLength));
            if (NULL == m_SemID)
            {
                messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_ALLOCATE_SEMID,
                    SAPDB_ToString (nameBufferLength),
                    semID);
                return Error;
            }

            RTE_BuildGlobalNameSpaceName (semID, m_SemID, &nameBufferLength);
        }
        else
        {
            messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_OPEN_UNNAMED);
            return Error;
        }


        /* Automatic Reset; Initial state: nonsignaled */
        m_Handle = OpenEvent (EVENT_ALL_ACCESS, FALSE, m_SemID);

        if (NULL == m_Handle)
        {
            messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_OPEN,
                m_SemID, 
                SAPDB_ToString (GetLastError ()));
            m_Handle = RTE_UNDEF_HANDLE;
            retCode = Error;
        }
    }
    return retCode;
}

/*---------------------------------------------------------------------------*/

#else

RTESync_BinarySemaphore::SemRC    RTESync_BinarySemaphore::Create   
(
    RTE_UserType const              userType,
    RTE_DBName const                serverDB,
//Kann mode nicht generell rausgenommen werden und durch 0660 ersetzt werden?
    int const                       mode,
    uid_t const                     uid,
    RTESync_SemaphoreID &           semID,
    SAPDBErr_MessageList &          messageList
)
{
    mode_t                          umask_old ;
    RTE_SystemRc                    rc = RTE_SYSTEMRC_NO_ERROR;
    union semun                     arg ;
    struct semid_ds                 sbuf ;
    RTE_Path                        path;

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Create", Sync_Trace, 9);


    strcpy (m_ServerDB, serverDB);
    m_UserType = userType;

    //Aus serverDb, userType und ID einen Name bilden z.B. "/us:<DB-Name>/s<ID>"
    //oder wir nehmen gleich den unten erzeugten ID-File Pfad!!!

    umask_old = umask ( 0 );

    m_Handle = semget (IPC_PRIVATE, 1, mode);

    /* won't use a zero id */
    if (0 == m_Handle)
    {
        SAPDBErr_MessageList                       tmpMessageList;

        //Ignoring any errors while closing
        Close (tmpMessageList);
        /* That we gain a zero again is nearly impossible because */
        /* the ID is not used again as long we have no full turn in the range of usable ID's */
        m_Handle = semget (IPC_PRIVATE, 1, mode); 
    }

    (void) umask (umask_old);

    if (RTE_UNDEF_HANDLE == m_Handle)
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_CREATE, 
            RTE_save_strerror (errno));
        return Error;
    }

    semID = m_SemID = (RTESync_SemaphoreID) m_Handle;

    /* transfer ownership */
    arg.buf = &sbuf ;
    rc = RTE_save_semctl (m_Handle, 0, IPC_STAT, SEMCTL_IPC_STAT(arg));
    if (RTE_SYSTEMRC_NO_ERROR != rc)
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_SEMCTL_STAT, 
            RTE_save_strerror(errno));
        return Error;
    }

    if ( sbuf.sem_perm.uid != uid )
    {
        sbuf.sem_perm.uid = uid ;
        arg.buf = &sbuf ;
        rc = RTE_save_semctl (m_Handle , 0 , IPC_SET , SEMCTL_IPC_SET(arg));
        if (RTE_SYSTEMRC_NO_ERROR != rc)
        {
            messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_SEMCTL_SET, 
                RTE_save_strerror(errno));
            return Error;
        }
    }

    if (!RTE_GetTagFileName (path, m_UserType, m_ServerDB, Semaphore, m_Handle, messageList))
    {
        messageList = messageList + SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_TAG_GET_FILENAME,
                                                          SAPDB_ToString (m_UserType),
                                                          SAPDB_ToString (Semaphore),
                                                          SAPDB_ToString (m_ServerDB),
                                                          SAPDB_ToString (m_Handle));
        return Error;
    }
    else
    {
        RTE_FileHandle      handle;

        umask_old = umask ( 0 );
        //Rechte? 0664 -> 0660
        handle = RTE_save_openCreate (path, O_CREAT, 0664);
        if (RTE_UNDEF_HANDLE == handle)
        {
            messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_CREATE_ID_FILE,
                path,
                RTE_save_strerror (errno));
            return Error;
        }

        RTE_save_close (handle);
        umask ( umask_old );
    }

	return NoError;
}

/*---------------------------------------------------------------------------*/

RTESync_BinarySemaphore::SemRC    RTESync_BinarySemaphore::Open   
(
    RTESync_SemaphoreID const       semID,
    RTE_UserType const              userType,
    RTE_DBName const                serverDB,
    SAPDBErr_MessageList &          messageList
)
{

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Open", Sync_Trace, 9);

    if (RTE_UNDEF_HANDLE == m_Handle)
    {
        strcpy (m_ServerDB, serverDB);
        m_UserType = userType;
        m_SemID = semID;
        m_Handle = (RTE_SemaphoreHandle) semID;
    }

	return NoError;
}

#endif

/*---------------------------------------------------------------------------*/

RTESync_BinarySemaphore::SemRC  RTESync_BinarySemaphore::Post    
(
    SAPDBErr_MessageList &      messageList
)
const
{

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Post", Sync_Trace, 9);

#if defined (_WIN32)
    if (!SetEvent (m_Handle))
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_POST,
                                                          m_SemID,
                                                          SAPDB_ToString (GetLastError()));
        return Error;
    }
#else
    union semun   arg;

	arg.val = 1;

    if (-1 == RTE_save_semctl (m_Handle, 0 , SETVAL , SEMCTL_SETVAL(arg)))
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_POST, 
                                                          SAPDB_ToString (m_SemID),
                                                          RTE_save_strerror (errno));
        return Error;
    }
#endif

    return NoError;
}

/*---------------------------------------------------------------------------*/

RTESync_BinarySemaphore::SemRC    RTESync_BinarySemaphore::Wait
(
    SAPDB_ULong const           timeout,
    SAPDBErr_MessageList &      messageList
)
const
{
    RTE_SystemRc            rc = RTE_SYSTEMRC_NO_ERROR;
    SemRC                   retCode = NoError;

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Wait", Sync_Trace, 9);

#if defined (_WIN32)
    switch (WaitForSingleObject (m_Handle, timeout))
    {
    case WAIT_OBJECT_0:
        break;

    case WAIT_TIMEOUT:  
        retCode = Timeout;
        break;

    default: // WAIT_FAILED:    
        rc = GetLastError();       
        break;
    }

    if ( rc != RTE_SYSTEMRC_NO_ERROR )
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_WAIT,
                                                          m_SemID,
                                                          SAPDB_ToString (rc));
        retCode = Error;
    }

#else
//No timeout supported yet
    struct sembuf       semWait;

    semWait.sem_num = 0;
    semWait.sem_op  = -1;
    semWait.sem_flg = SEM_UNDO;

    if ( -1 == RTE_save_semop (m_Handle, &semWait, 1))
    {
        messageList = SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_WAIT,
                                                          SAPDB_ToString (m_SemID),
                                                          RTE_save_strerror (errno));
        retCode = Error;
    }
#endif

    return retCode;
}

/*---------------------------------------------------------------------------*/

RTESync_BinarySemaphore::SemRC    RTESync_BinarySemaphore::Close   
(
    SAPDBErr_MessageList &      messageList

)
{
    SAPDBErr_MessageList            tmpMessageList;
    SemRC                           retCode = NoError;

    SAPDBTRACE_METHOD_DEBUG ("RTESync_BinarySemaphore::Close", Sync_Trace, 9);

	if (RTE_UNDEF_HANDLE != m_Handle) 
    {
#if defined (_WIN32)
        if ( !CloseHandle (m_Handle))
        {
            tmpMessageList = tmpMessageList + SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_CLOSE_HANDLE, 
                                                              m_SemID,
                                                              SAPDB_ToString (GetLastError()));
            retCode = Error;
        }

#else
        union semun arg;
        arg.val = 0;

        if (-1 != RTE_save_semctl (m_Handle, 0 , IPC_RMID, SEMCTL_SETVAL(arg)))
        {
            tmpMessageList = tmpMessageList + SAPDBErr_MessageList (RTE_CONTEXT, RTEERR_SYNC_SEM_CLOSE_HANDLE, 
                                                              SAPDB_ToString (m_SemID),
                                                              RTE_save_strerror (errno));
            retCode = Error;
        }

        if (RTE_SYNC_UNDEF_SEM_ID != m_SemID)
        {
            if (!RTE_RemoveTagFile (m_UserType, m_ServerDB, Semaphore, m_SemID, messageList))
            {
                retCode = Error;
            }
        }

#endif
    }

#if defined (_WIN32)
    if (RTE_SYNC_UNDEF_SEM_ID != m_SemID)
    {
        RTEMem_RteAllocator::Instance().Deallocate (m_SemID);
    }
#endif

    m_Handle = RTE_UNDEF_HANDLE;
    m_SemID = RTE_SYNC_UNDEF_SEM_ID;

    if (NoError != retCode)
    {
        messageList = tmpMessageList;
    }

    return retCode;
}






