/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_SYSVSEMAPHORE_H
#define __IWEAR_SYSVSEMAPHORE_H

extern "C"
{
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
}

#ifdef _SEM_SEMUN_UNDEFINED
union semun
{
    int val;                           //<= value for SETVAL
    struct semid_ds *buf;              //<= buffer for IPC_STAT & IPC_SET
    unsigned short int *array;         //<= array for GETALL & SETALL
    struct seminfo *__buf;             //<= buffer for IPC_INFO
};
#endif

#if 0
struct semop_param 
{
    unsigned short  semval;   /* semaphore value */
    unsigned short  semzcnt;  /* # waiting for zero */
    unsigned short  semncnt;  /* # waiting for increase */
    pid_t           sempid;   /* process that did last op */
};
#endif

namespace iwear
{
/**
 * This is a simple wrapper for a POSIX semaphore.
 */
class SysVSemaphore
{
private:
protected:
    /**
     * The SysV Semaphore set we wrap
     */
    int semsetid;
    /**
     * The id of the SysV Semaphore in the set we map.
     */
    int semid;
public:
    /**
     * Constructs a semaphore with initial count of 0, which can optionally be
     * set.
     */
    SysVSemaphore( int32_t key, int32_t snum, int v = 0);
    /**
     * Destroys the semaphore.
     */
    ~SysVSemaphore();
    /**
     * Posts the semaphore, i.e. increases the count on the semaphore.
     */
    int Post( void );
    /**
     * Suspends the current thread until the semaphore count is non-zero. Then
     * it decreases the semaphore count.
     */
    int Wait( void );
    /**
     * Tries to decrease the count. If it works, it returns true, false otherwise.
     */
    int TryWait( void );
    /**
     * Returns the current value of the semaphore.
     */
    int Value( void );

    pid_t LastPid( void );

    int SetValue( int val );
};

SysVSemaphore::~SysVSemaphore( )
{
}

SysVSemaphore::SysVSemaphore( int32_t key, int32_t snum, int v )
    : semid(snum)
{
    semsetid = semget(key,snum,IPC_CREAT|S_IRUSR|S_IWUSR);
}

int SysVSemaphore::Post( void )
{
    struct sembuf sb;
    memset(&sb,0,sizeof(sb));
    sb.sem_num = semid;
    sb.sem_op = 1;
    sb.sem_flg = SEM_UNDO;
    
    return semop(semsetid,&sb,1);
}

int SysVSemaphore::Wait( void )
{    
    struct sembuf sb;
    memset(&sb,0,sizeof(sb));
    sb.sem_num = semid;
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO;
    
    return semop(semsetid,&sb,1);
}

int SysVSemaphore::TryWait( void )
{
    struct sembuf sb;
    memset(&sb,0,sizeof(sb));
    sb.sem_num = semid;
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO | IPC_NOWAIT;
    
    return semop(semsetid,&sb,1);
}

int SysVSemaphore::Value( void )
{
    return semctl(semsetid,semid,GETVAL);
}

pid_t SysVSemaphore::LastPid( void )
{
    return semctl(semsetid,semid,GETPID);
}

int SysVSemaphore::SetValue( int val )
{
    return semctl(semsetid,semid,SETVAL,val);
}

}
#endif
