cannam@62: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@62: cannam@62: /* cannam@62: QM DSP Library cannam@62: cannam@62: Centre for Digital Music, Queen Mary, University of London. cannam@62: This file copyright Chris Cannam, used with permission. cannam@62: */ cannam@62: cannam@62: #ifndef _THREAD_H_ cannam@62: #define _THREAD_H_ cannam@62: cannam@62: #ifdef _WIN32 cannam@62: #include cannam@62: #else /* !_WIN32 */ cannam@62: #ifdef USE_PTHREADS cannam@62: #include cannam@62: #endif /* USE_PTHREADS */ cannam@62: #endif /* !_WIN32 */ cannam@62: cannam@62: #include cannam@62: cannam@62: //#define DEBUG_THREAD 1 cannam@62: //#define DEBUG_MUTEX 1 cannam@62: //#define DEBUG_CONDITION 1 cannam@62: cannam@62: class Thread cannam@62: { cannam@62: public: cannam@62: #ifdef _WIN32 cannam@62: typedef HANDLE Id; cannam@62: #else cannam@62: #ifdef USE_PTHREADS cannam@62: typedef pthread_t Id; cannam@62: #endif cannam@62: #endif cannam@62: cannam@62: Thread(); cannam@62: virtual ~Thread(); cannam@62: cannam@62: Id id(); cannam@62: cannam@62: void start(); cannam@62: void wait(); cannam@62: cannam@62: static bool threadingAvailable(); cannam@62: cannam@62: protected: cannam@62: virtual void run() = 0; cannam@62: cannam@62: private: cannam@62: #ifdef _WIN32 cannam@62: HANDLE m_id; cannam@62: bool m_extant; cannam@62: static DWORD WINAPI staticRun(LPVOID lpParam); cannam@62: #else cannam@62: #ifdef USE_PTHREADS cannam@62: pthread_t m_id; cannam@62: bool m_extant; cannam@62: static void *staticRun(void *); cannam@62: #endif cannam@62: #endif cannam@62: }; cannam@62: cannam@62: class Mutex cannam@62: { cannam@62: public: cannam@62: Mutex(); cannam@62: ~Mutex(); cannam@62: cannam@62: void lock(); cannam@62: void unlock(); cannam@62: bool trylock(); cannam@62: cannam@62: private: cannam@62: #ifdef _WIN32 cannam@62: HANDLE m_mutex; cannam@62: #ifndef NO_THREAD_CHECKS cannam@62: DWORD m_lockedBy; cannam@62: #endif cannam@62: #else cannam@62: #ifdef USE_PTHREADS cannam@62: pthread_mutex_t m_mutex; cannam@62: #ifndef NO_THREAD_CHECKS cannam@62: pthread_t m_lockedBy; cannam@62: bool m_locked; cannam@62: #endif cannam@62: #endif cannam@62: #endif cannam@62: }; cannam@62: cannam@62: class MutexLocker cannam@62: { cannam@62: public: cannam@62: MutexLocker(Mutex *); cannam@62: ~MutexLocker(); cannam@62: cannam@62: private: cannam@62: Mutex *m_mutex; cannam@62: }; cannam@62: cannam@62: class Condition cannam@62: { cannam@62: public: cannam@62: Condition(std::string name); cannam@62: ~Condition(); cannam@62: cannam@63: // Condition bundles a pthread-style condition variable and mutex cannam@63: // into one class. cannam@62: cannam@63: // To wait on a condition, call lock(), test termination variables cannam@63: // as appropriate, and then wait(). The condition will be cannam@63: // unlocked for the duration of the wait() call, which will end cannam@63: // when the condition is signalled. The condition will be locked cannam@63: // again when wait() returns. cannam@63: // cannam@63: // To signal a condition, call signal(). If the waiting thread cannam@63: // will be performing tests between its own lock() and wait(), cannam@63: // then the signalling thread should also lock() before it signals cannam@63: // (and then unlock afterwards). If the signalling thread always cannam@63: // locks the mutex during signalling, then the waiting thread cannam@63: // knows that signals will only happen during wait() and not be cannam@63: // missed at other times. cannam@63: cannam@62: void lock(); cannam@62: void unlock(); cannam@62: void wait(int us = 0); cannam@62: cannam@62: void signal(); cannam@62: cannam@62: private: cannam@62: cannam@62: #ifdef _WIN32 cannam@62: HANDLE m_mutex; cannam@62: HANDLE m_condition; cannam@62: bool m_locked; cannam@62: #else cannam@62: #ifdef USE_PTHREADS cannam@62: pthread_mutex_t m_mutex; cannam@62: pthread_cond_t m_condition; cannam@62: bool m_locked; cannam@62: #endif cannam@62: #endif cannam@62: #ifdef DEBUG_CONDITION cannam@62: std::string m_name; cannam@62: #endif cannam@62: }; cannam@62: cannam@63: class AsynchronousTask : public Thread cannam@63: { cannam@63: public: cannam@63: AsynchronousTask() : cannam@63: m_todo("AsynchronousTask: task to perform"), cannam@63: m_done("AsynchronousTask: task complete"), cannam@63: m_inTask(false), cannam@63: m_finishing(false) cannam@63: { cannam@63: start(); cannam@63: } cannam@63: virtual ~AsynchronousTask() cannam@63: { cannam@63: m_finishing = true; cannam@63: m_todo.signal(); cannam@63: wait(); cannam@63: } cannam@63: cannam@63: // subclass must provide methods to request task and obtain cannam@63: // results cannam@63: cannam@63: protected: cannam@63: void startTask() { cannam@63: m_todo.lock(); cannam@63: m_inTask = true; cannam@63: m_todo.signal(); cannam@63: m_done.lock(); cannam@63: m_todo.unlock(); cannam@63: } cannam@63: void awaitTask() { cannam@63: while (m_inTask) m_done.wait(); cannam@63: m_done.unlock(); cannam@63: } cannam@63: cannam@63: virtual void performTask() = 0; cannam@63: cannam@63: private: cannam@63: virtual void run() { cannam@63: m_todo.lock(); cannam@63: while (!m_finishing) { cannam@63: while (!m_inTask && !m_finishing) m_todo.wait(); cannam@63: if (m_finishing) break; cannam@63: if (m_inTask) { cannam@63: performTask(); cannam@63: m_inTask = false; cannam@63: m_done.signal(); cannam@63: } cannam@63: } cannam@63: m_todo.unlock(); cannam@63: } cannam@63: cannam@63: Condition m_todo; cannam@63: Condition m_done; cannam@63: bool m_inTask; cannam@63: bool m_finishing; cannam@63: }; cannam@63: cannam@62: #endif