    /*

    Copyright (C) 2001 Stefan Westerfeld
                       stefan@space.twc.de

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
  
    This library 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
    Library General Public License for more details.
   
    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

    */

#ifndef ARTS_MCOP_THREAD_H
#define ARTS_MCOP_THREAD_H

#include "arts_export.h"

/*
 * BC - Status (2002-03-08): SystemThreads, Thread, Mutex, ThreadCondition,
 * Semaphore
 *
 * These classes are kept binary compatible. As the threading implementation
 * can be changed, also
 *
 * Thread_impl, Mutex_impl, ThreadCondition_impl and Semaphore_impl need
 * to remain as they are. The implementation of these depends on what thread
 * system is used, and can attach the required data as necessary to the
 * base classes. So i.e. a posix mutex can contain the pthread_mutex_t in
 * the PosixMutex_impl.
 */

namespace Arts {

class Mutex_impl;
class Thread_impl;
class ThreadCondition_impl;
class Thread;
class Semaphore_impl;

/**
 * Encapsulates the operating system threading facilities
 */
class ARTS_EXPORT SystemThreads {
public:
	static SystemThreads *the();
	static bool init(SystemThreads *the);

	/**
	 * Check whether there is threading support available
	 *
	 * If there is no real threading support, the Threading classes try to
	 * gracefully degrade the performance. For instance, locking a mutex will
	 * do * nothing, and calling the start() function of a Thread will execute
	 * it's run function.
	 *
	 * @returns true if there are real threads 
	 */
	static bool supported();

	/**
	 * Check wether the current thread is the main thread
	 *
	 * The main thread is the thread that the application's main() was
	 * executed in. The IOManager event loop will only run in the main
	 * thread.
	 */
	virtual bool isMainThread() = 0;

	virtual Mutex_impl *createMutex_impl() = 0;
	virtual Mutex_impl *createRecMutex_impl() = 0;
	virtual Thread_impl *createThread_impl(Thread *thread) = 0;
	virtual ThreadCondition_impl *createThreadCondition_impl() = 0;
	virtual Semaphore_impl *createSemaphore_impl(int, int) = 0;
	virtual ~SystemThreads();

	/**
	 * Returns a pointer to the current thread, or a null pointer if
	 * we're the main thread (isMainThread() is true).
	 */
	virtual Thread *getCurrentThread() = 0;
};

/**
 * Base class for platform specific thread code
 */
class ARTS_EXPORT Thread_impl
{
public:
	virtual void setPriority(int) =0;
	virtual void start() = 0;
	virtual void waitDone() = 0;
	virtual ~Thread_impl();
};

/**
 * Base class for platform specific mutex code
 */
class ARTS_EXPORT Mutex_impl {
public:
	virtual void lock() = 0;
	virtual bool tryLock() = 0;
	virtual void unlock() = 0;
	virtual ~Mutex_impl();
};

/**
 * Base class for platform specific thread condition code
 */
class ARTS_EXPORT ThreadCondition_impl {
public:
	virtual void wakeOne() = 0;
	virtual void wakeAll() = 0;
	virtual void wait(Mutex_impl *impl) = 0;
	virtual ~ThreadCondition_impl();
};

class ARTS_EXPORT Semaphore_impl {
public:
	virtual void wait() = 0;
	virtual int tryWait() = 0;
	virtual void post() = 0;
	virtual int getValue() = 0;
	virtual ~Semaphore_impl();
};

/**
 * A thread of execution
 *
 * Example for implementing a thread:
 *
 * <pre>
 * class Counter : public Arts::Thread 
 * {
 * public:
 *   void run() {
 *     for(int i = 0;i < 10;i++)
 *     {
 *       printf("%d\n",i+1);
 *       sleep(1);
 *     }
 *   }
 * };            // start the thread with Counter c; c.start();
 * </pre>
 */
class ARTS_EXPORT Thread {
private:
	Thread_impl *impl;
		 
public:
	Thread() : impl(SystemThreads::the()->createThread_impl(this))
	{
	}
	
	virtual ~Thread();

	/**
	 * set the priority parameters for the thread
	 * 
	 * FIXME: what should be minimum, maximum, recommended?
	 */
	inline void setPriority(int priority) {
		impl->setPriority(priority);
	}

	/**
	 * starts the run() method in a thread
	 */
	inline void start()
	{
		impl->start();
	}

	/**
	 * waits until the thread is executed completely
	 */
	inline void waitDone()
	{
		impl->waitDone();
	}

	/**
	 * implement this method, if you want to create an own thread - then
	 * you can simply call thread.start() to start execution of run() in
	 * a seperate thread
	 */
	virtual void run() = 0;
};

/**
 * A mutex
 *
 * To protect a critical section, you can use a mutex, which will ensure that
 * only one thread at a time can lock it. Here is an example for a thread-safe
 * random number generator:
 *
 * <pre>
 * class RandomGenerator {
 *   Arts::Mutex mutex;
 *   long seed;
 * public:
 *   long get() {
 *     mutex.lock();
 *     // do complicated calculation with seed here
 *     mutex.unlock();
 *     return seed;
 *   }
 * };
 * </pre>
 */
class ARTS_EXPORT Mutex {
private:
	Mutex_impl *impl;
	friend class ThreadCondition;

public:
	/**
	 * constructor
	 *
	 * @param recursive whether to create a recursive mutex (may be locked by
	 *                  the same thread more than once), or a normal mutex
	 */
	inline Mutex(bool recursive = false)
		: impl(recursive?SystemThreads::the()->createRecMutex_impl()
						:SystemThreads::the()->createMutex_impl())
	{
	}

	/**
	 * destructor
	 */
	virtual ~Mutex();

	/**
	 * locks the mutex
	 */
	inline void lock() {
		impl->lock();
	}

	/**
	 * tries to lock the mutex, returning immediately in any case (even if
	 * mutex is locked by another thread)
	 *
	 * @returns true if successful (mutex locked), false otherwise
	 */
	inline bool tryLock() {
		return impl->tryLock();
	}

	/**
	 * unlocks the mutex
	 */
	inline void unlock() {
		impl->unlock();
	}
};

/**
 * A thread condition
 *
 * Thread conditions are used to let a different thread know that a certain
 * condition might have changed. For instance, if you have a thread that
 * waits until a counter exceeds a limit, the thread would look like this:
 *
 * <pre>
 * class WaitCounter : public Arts::Thread 
 * {
 *   int counter;
 *   Arts::Mutex mutex;
 *   Arts::ThreadCondition cond;
 * 
 * public:
 *   WaitCounter() : counter(0) {}
 *
 *   void run() {  // run will terminate once the counter reaches 20
 *     mutex.lock();
 *     while(counter < 20)
 *       cond.wait(mutex);
 *     mutex.unlock();
 *   }
 *
 *   void inc() {  // inc will increment the counter and indicate the change
 *     mutex.lock();
 *     counter++;
 *     cond.wakeOne();
 *     mutex.unlock();
 *   }
 * };
 * </pre>
 */
class ARTS_EXPORT ThreadCondition {
private:
	ThreadCondition_impl *impl;
		 
public:
	ThreadCondition()
		: impl(SystemThreads::the()->createThreadCondition_impl())
	{
	}
	
	virtual ~ThreadCondition();

	/**
	 * wakes one waiting thread
	 */
	inline void wakeOne()
	{
		impl->wakeOne();
	}

	/**
	 * wakes all waiting threads
	 */
	inline void wakeAll()
	{
		impl->wakeAll();
	}

	/**
	 * Waits until the condition changes. You will need to lock the mutex
	 * before calling this. Internally it will unlock the mutex (to let
	 * others change the condition), and relock it once the wait succeeds.
	 */
	inline void wait(Mutex& mutex)
	{
		impl->wait(mutex.impl);
	}
};

class ARTS_EXPORT Semaphore {
private:
	Semaphore_impl *impl;

public:
	Semaphore(int shared=0, int count=0)
	{
		impl = SystemThreads::the()->createSemaphore_impl(shared, count);
	}

	virtual ~Semaphore();

	inline void wait()
	{
		impl->wait();
	}

	inline int tryWait()
	{
		return impl->tryWait();
	}

	inline void post()
	{
		impl->post();
	}

	inline int getValue()
	{
		return impl->getValue();
	}
};

}

#endif /* ARTS_MCOP_THREAD_H */
