/* 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: * *
* 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();
*
*/
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:
*
*
* class RandomGenerator {
* Arts::Mutex mutex;
* long seed;
* public:
* long get() {
* mutex.lock();
* // do complicated calculation with seed here
* mutex.unlock();
* return seed;
* }
* };
*
*/
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:
*
*
* 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();
* }
* };
*
*/
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 */