/* 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 */