/*
 * The UDS Collection
 * Copyright (C) 2000, 2001  Julian v. Bock
 *
 * 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
 *
 *	(See file COPYING for more information)
 */

#ifndef _uds_thread_hh
#define _uds_thread_hh

#ifndef _REENTRANT
#error "you must #define _REENTRANT"
#endif


// includes

#include <uds/action.hh>
#include <uds/refcounter.hh>
#include <uds/stdexcpt.hh>
#include <uds/time.hh>
#include <pthread.h>
#include <semaphore.h>
#include <stack>


namespace uds
{

/** mutex class. Note: the mutex methods are all const so you don't have to
	make them mutable to protect const operations.
	\author Julian v. Bock */
class Mutex
{
public:
	/** \param t mutex type */
	Mutex() { pthread_mutex_init( &mut, 0 ); }
	
	/** destructor */
	~Mutex() { pthread_mutex_destroy( &mut ); }
	
	/** locks the mutex */
	void
	Lock() const;
	
	/** tries to lock the mutex
		\return 0 on success, otherwise an error code */
	int
	TryLock() const { return pthread_mutex_trylock( &mut ); }
	
	/** unlocks the mutex */
	void
	UnLock() const;
	
	/** \return mutex */
	pthread_mutex_t&
	Get() const { return mut; }
	
private:
	/// mutex
	mutable pthread_mutex_t mut;
	
	void*
	operator new ( size_t );
};

/** mutex lock class
	\author Julian v. Bock */
class MutexLock
{
public:
	/** \param m mutex */
	MutexLock( const Mutex& m ) : mut( m ) { mut.Lock(); }
	
	/** destructor */
	~MutexLock() { mut.UnLock(); }
	
private:
	/// the mutex
	const Mutex& mut;
	
	void*
	operator new ( size_t );
};

/** mutex lock class. makes sure that the mutex gets unlocked even if the
	thread is cancelled before the lock class is destroyed.
	\author Julian v. Bock */
class CMutexLock
{
public:
	/** \param m mutex */
	CMutexLock( const Mutex& m );
	
	/** destructor */
	~CMutexLock();
	
private:
	/// the mutex
	const Mutex& mut;
};


/** condition variable class
	\author Julian v. Bock */
class CondVar
{
public:
	/** default constructor */
	CondVar();
	
	/** destructor */
	~CondVar();
	
	/** restarts one of the threads that are waiting on the condition
		variable */
	void
	Signal() const { pthread_cond_signal( &cond ); }
	
	/** restarts all the threads that are waiting on the condition
		variable */
	void
	Broadcast() const { pthread_cond_broadcast( &cond ); }
	
	/** waits for the condition variable to be signaled. The thread execution
		is suspended. */
	void
	Wait() const { pthread_cond_wait( &cond, &mut.Get() ); }
	
	/** waits as Wait() does, but also bounds the duration of the wait.
		\param t time
		\return 0 if the condition was signaled, otherwise an error code is
			returned */
	int
	TimedWait( const TimeSpec& t ) const
		{ return pthread_cond_timedwait( &cond, &mut.Get(), &t.to_timespec() ); }
	
	/** \return associated mutex */
	const Mutex&
	AMutex() const { return mut; }
	
private:
	/// condition variable
	mutable pthread_cond_t	cond;
	/// associated mutex
	Mutex					mut;
};


/** semaphore class
	\author Julian v. Bock */
class Semaphore
{
public:
	/** \param s 'is shared' flag
		\param v initial value */
	Semaphore( bool s = false, int v = 0 );
	
	/** destructor */
	~Semaphore();
	
	/** suspends the calling thread until the semaphore has non-zero count.
		It then atomically decreases the semaphore count. */
	void
	Wait() const { sem_wait( &sem ); }
	
	/** non-blocking variant of Wait()
		\return 0 on success */
	int
	TryWait() const { return sem_trywait( &sem ); }
	
	/** atomically increases the count of the semaphore. This function
		never blocks. */
	void
	Post() const;
	
	/** \return value of the semaphore */
	int
	Value() const;
	
private:
	/// semaphore
	mutable sem_t sem;
};


/** thread attribute class
	\author Julian v. Bock */
class ThreadAttr
{
public:
	/** default constructor */
	ThreadAttr();
	
	/** destructor */
	~ThreadAttr();
	
	/** \return detachstate */
	int
	DetachState() const;
	
	/** sets the detachstate
		\param v detachstate */
	void
	DetachState( int v ) { pthread_attr_setdetachstate( &attr, v ); }
	
	/** \return scheduling policy */
	int
	SchedPolicy() const;
	
	/** sets the scheduling policy
		\param v scheduling policy */
	void
	SchedPolicy( int v ) { pthread_attr_setschedpolicy( &attr, v ); }
	
	/** \return scheduling parameter */
	int
	SchedParam() const;
	
	/** sets the scheduling parameter
		\param v scheduling parameter */
	void
	SchedParam( int v );
	
	/** \return inheritsched parameter */
	int
	InheritSched() const;
	
	/** sets the inheritsched parameter
		\param v inheritsched parameter */
	void
	InheritSched( int v ) { pthread_attr_setinheritsched( &attr, v ); }
	
	/** \return scheduling contention scope */
	int
	Scope() const;
	
	/** sets the scheduling contention scope
		\param v scheduling contention scope */
	void
	Scope( int v ) { pthread_attr_setscope( &attr, v ); }
	
	const pthread_attr_t&
	Get() { return attr; }
	
private:
	/// thread attributes
	pthread_attr_t attr;
};


/** thread class
	\author Julian v. Bock */
class Thread : public RefCounter<>
{
public:
	typedef GC_Ptr< Thread > GC_Ptr;
	typedef AnyAction CleanupHandler;
	typedef Action< void* > Routine;
	
	/** \param start start function [object]
		\param attr thread attributes */
	template < class F >
	Thread( F start, pthread_attr_t* attr = 0 );
	
	/** sends a cancellation request to the thread */
	void
	Cancel();
	
	/** joins another thread
		\param th thread
		\return return value */
	void*
	Join( const Thread& th );
	
	/** sets the cancellation state
		\param s state
		\return old state */
	int
	SetCancelState( int t );
	
	/** sets the type of responses to cancellation requests
		\param t type
		\return old type */
	int
	SetCancelType( int t );
	
	/** detaches the thread */
	void
	Detach();
	
	/** registers a new cleanup handler
		\param ch cleanup handler */
	void
	PushCleanupHandler( const CleanupHandler& ch );
	
	/** removes a cleanup handler
		\param e 'execute cleanup handler' flag */
	void
	PopCleanupHandler( bool e = false );
	
	/** \return thread identifier */
	const pthread_t&
	Get() const { return thread; }
	
	/** \return Thread instance for the calling thread */
	static GC_Ptr
	Self();
	
	/** \return true if at least one thread has been created */
	static bool
	Threaded();
	
private:
	/** destructor */
	~Thread();
	
	class launch_info
	{
	public:
		/// start function [object]
		Routine		start;
		/// Thread object
		Thread*		_this;
		
		/** \param s start function
			\param t Thread object */
		launch_info( const Routine& s, Thread* t );
		
		/** destructor */
		~launch_info() {}
	};
	
	typedef stack< CleanupHandler > CHStack;
	
	/// thread
	pthread_t	thread;
	/// cleanup handler stack
	CHStack		chs;
	/// mutex
	Mutex		mutex;
	
	/** \param tid thread id */
	Thread( pthread_t tid );
	
	static void*
	launch( launch_info* arg );
	
	static void
	deactivate( Thread* _this );
	
	friend class GC_Ptr;
};

template < class F >
Thread::Thread( F start, pthread_attr_t* attr )
{
	if ( pthread_create( &thread, attr, &launch, new launch_info( start, this ) ))
		Throw( system_error( "unable to create new thread" ) );
}

}

#endif // _uds_thread_hh
