/**
 * @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_IWEAR_RWLOCK_H
#define __IWEAR_IWEAR_RWLOCK_H

//TODO: Add conditionals for different OSs
extern "C" {
#include <pthread.h>
#ifndef HAVE_PTHREAD
#define HAVE_PTHREAD
#endif
}

namespace iwear
{

    namespace detail 
    {
// Here typedef the type available on your OS
#ifdef HAVE_PTHREAD
typedef pthread_rwlock_t rwlock_t;
#endif
/**
 * This implements a thin wrapper around basic read write locks provided by the
 * OS. If possible, it will map directly to the functionality of the OS. If
 * this is not possible, an alternative, but most likely less effective
 * implementation will be used.
 * For simplicity of implementation, the functions of this class will never
 * throw themselves, but return the return code of the pthread functions. Since
 * its anyways recommended to use the scoped locking facilities, which will
 * throw, this is probably not much of a problem.
 */
class rwlock_base
{
private:
protected:
    rwlock_t _rwlock;
public:
    /**
     * This sets the preference of what shall have priority on this lock. The
     * exact semantics of those settings will differ from OS to OS, up to being
     * indistinguishable.
     */
    enum preference 
    {
	prefer_reader,
	prefer_writer
    };

    rwlock( preference pf );

    rwlock( void );

    ~rwlock( void );

    int rdlock( void );

    int rdlock( float );

    int try_rdlock( void );

    int wrlock( void );

    int wrlock( float );

    int try_wrlock( void );

    int unlock( void );
};
    } // of namespace detail

struct rwlock: detail::rwlock_base
{
    typedef scoped_lock<rwlock_base,
	    &rwlock_base::unlock,
	    &rwlock_base::rdlock,
	    &rwlock_base::try_rdlock,
	    &rwlock_base::rdlock> 
		scoped_rdlock;

    typedef scoped_lock<rwlock_base,
	    &rwlock_base::unlock,
	    &rwlock_base::wrlock,
	    &rwlock_base::try_wrlock,
	    &rwlock_base::wrlock> 
		scoped_wrlock;
};

#ifdef HAVE_PTHREAD

    namespace detail {

inline rwlock::rwlock( preference pf )
{
    pthread_rwlockattr_t attr;

    int pref = 0;
    switch( pf )
    {
	case prefer_reader:
	    pref = PTHREAD_RWLOCK_PREFER_READER_NP;
	    break;
	case prefer_writer:
	    pref = PTHREAD_RWLOCK_PREFER_WRITER_NP;
	    break;
    }
    pthread_rwlockattr_setkind_np(&attr,pref);
    pthread_rwlock_init(&_rwlock,&attr);
    pthread_attr_destroy(&attr); ///@todo I am not really sure whether this is portable.
}

inline rwlock::rwlock( void )
{
    pthread_rwlock_init(&_rwlock,NULL);
}

inline rwlock::~rwlock( void )
{
    pthread_rwlock_destroy(&_rwlock);
}

inline int rwlock::rdlock( void )
{
}

inline int rwlock::rdlock( float to )
{

    struct timespec timeout;
    clock_settimeout(timeout,to);

    return pthread_rwlock_timedrdlock(&_rwlock,&timeout);
}

inline int rwlock::try_rdlock( void )
{
    return pthread_rwlock_tryrdlock(&_rwlock);
}

inline int rwlock::wrlock( float )
{
}

inline int rwlock::wrlock( void )
{
    return pthread_rwlock_wrlock(&_rwlock);
}

inline int rwlock::try_wrlock( void )
{
    return pthread_rwlock_trywrlock(&_rwlock);
}

inline int rwlock::unlock( void )
{
    return pthread_rwlock_unlock(&_rwlock);
}
    } // of namespace detail
#else
#error Sorry, we don't have an rwlock implementation for you OS.
#endif
}
#endif

