diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2011-03-02 12:37:39 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2011-03-02 12:37:39 +0000 |
commit | 3deba1921bcf5bddd8bea9846a203c92b6c9239d (patch) | |
tree | 8b2e00405337396918ff28e282df14e958b84bb9 /libs/qm-dsp/thread | |
parent | fa41cfef580b2c8c04adec5b47d6cfa415ca6251 (diff) |
add queen mary DSP library
git-svn-id: svn://localhost/ardour2/branches/3.0@9029 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/qm-dsp/thread')
-rw-r--r-- | libs/qm-dsp/thread/AsynchronousTask.h | 110 | ||||
-rw-r--r-- | libs/qm-dsp/thread/BlockAllocator.h | 189 | ||||
-rw-r--r-- | libs/qm-dsp/thread/Thread.cpp | 643 | ||||
-rw-r--r-- | libs/qm-dsp/thread/Thread.h | 149 |
4 files changed, 1091 insertions, 0 deletions
diff --git a/libs/qm-dsp/thread/AsynchronousTask.h b/libs/qm-dsp/thread/AsynchronousTask.h new file mode 100644 index 0000000000..d9d7d872c9 --- /dev/null +++ b/libs/qm-dsp/thread/AsynchronousTask.h @@ -0,0 +1,110 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + QM DSP Library + + Centre for Digital Music, Queen Mary, University of London. + This file Copyright 2009 QMUL. +*/ + +#ifndef _ASYNCHRONOUS_TASK_H_ +#define _ASYNCHRONOUS_TASK_H_ + +#include "Thread.h" + +#include <iostream> + +/** + * AsynchronousTask provides a thread pattern implementation for + * threads which are used to perform a series of similar operations in + * parallel with other threads of the same type. + * + * For example, a thread used to calculate FFTs of a particular block + * size in the context of a class that needs to calculate many block + * sizes of FFT at once may be a candidate for an AsynchronousTask. + * + * The general use pattern is: + * + * caller -> request thread A calculate something + * caller -> request thread B calculate something + * caller -> request thread C calculate something + * caller -> wait for threads A, B, and C + * + * Here threads A, B, and C may be AsynchronousTasks. An important + * point is that the caller must be prepared to block when waiting for + * these threads to complete (i.e. they are started asynchronously, + * but testing for completion is synchronous). + */ +class AsynchronousTask : public Thread +{ +public: + AsynchronousTask() : + m_todo("AsynchronousTask: task to perform"), + m_done("AsynchronousTask: task complete"), + m_inTask(false), + m_finishing(false) + { + start(); + } + virtual ~AsynchronousTask() + { + m_todo.lock(); + m_finishing = true; + m_todo.signal(); + m_todo.unlock(); + wait(); + } + + // Subclass must provide methods to request task and obtain + // results, which the caller calls. The method that requests a + // new task should set up any internal state and call startTask(), + // which then calls back on the subclass implementation of + // performTask from within its work thread. The method that + // obtains results should call awaitTask() and then return any + // results from internal state. + +protected: + void startTask() { + m_done.lock(); + m_todo.lock(); + m_inTask = true; + m_todo.signal(); + m_todo.unlock(); + } + void awaitTask() { + m_done.wait(); + m_done.unlock(); + } + + virtual void performTask() = 0; + +private: + virtual void run() { + m_todo.lock(); + while (1) { + while (!m_inTask && !m_finishing) { + m_todo.wait(); + } + if (m_finishing) { + m_done.lock(); + m_inTask = false; + m_done.signal(); + m_done.unlock(); + break; + } + performTask(); + m_done.lock(); + m_inTask = false; + m_done.signal(); + m_done.unlock(); + } + m_todo.unlock(); + } + + Condition m_todo; + Condition m_done; + bool m_inTask; + bool m_finishing; +}; + +#endif diff --git a/libs/qm-dsp/thread/BlockAllocator.h b/libs/qm-dsp/thread/BlockAllocator.h new file mode 100644 index 0000000000..5c311d615d --- /dev/null +++ b/libs/qm-dsp/thread/BlockAllocator.h @@ -0,0 +1,189 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + QM DSP Library + + Centre for Digital Music, Queen Mary, University of London. + + This file is derived from the FSB Allocator by Juha Nieminen. The + underlying method is unchanged, but the class has been refactored + to permit multiple separate allocators (e.g. one per thread) + rather than use a single global one (and to fit house style). + +Copyright (c) 2008 Juha Nieminen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef _BLOCK_ALLOCATOR_H_ +#define _BLOCK_ALLOCATOR_H_ + +#include <cstdlib> + +/** + * BlockAllocator is a simple allocator for fixed-size (usually small) + * chunks of memory. The size of an element is specified in the + * BlockAllocator constructor, and the functions allocate() and + * deallocate() are used to obtain and release a single element at a + * time. + * + * BlockAllocator may be an appropriate class to use in situations + * involving a very large number of allocations and deallocations of + * simple, identical objects across multiple threads (a hard situation + * for a generic system malloc implementation to handle well). Retain + * one BlockAllocator per thread (the class itself is not + * thread-safe), and ensure that each thread uses its own allocator + * exclusively. + * + * BlockAllocator is based on Juha Nieminen's more general + * FSBAllocator. + */ +class BlockAllocator +{ +public: + typedef std::size_t data_t; + + BlockAllocator(int elementSize) : m_sz(elementSize) { } + + void * + allocate() + { + if (m_freelist.empty()) { + m_freelist.push_back(m_blocks.data.size()); + m_blocks.data.push_back(Block(this)); + } + + const data_t index = m_freelist.back(); + Block &block = m_blocks.data[index]; + void *retval = block.allocate(index); + if (block.isFull()) m_freelist.pop_back(); + + return retval; + } + + void + deallocate(void *ptr) + { + if (!ptr) return; + + data_t *unitPtr = (data_t *)ptr; + const data_t blockIndex = unitPtr[elementSizeInDataUnits()]; + Block& block = m_blocks.data[blockIndex]; + + if (block.isFull()) m_freelist.push_back(blockIndex); + block.deallocate(unitPtr); + } + +private: + inline data_t elementsPerBlock() const { + return 512; + } + inline data_t dataSize() const { + return sizeof(data_t); + } + inline data_t elementSizeInDataUnits() const { + return (m_sz + (dataSize() - 1)) / dataSize(); + } + inline data_t unitSizeInDataUnits() const { + return elementSizeInDataUnits() + 1; + } + inline data_t blockSizeInDataUnits() const { + return elementsPerBlock() * unitSizeInDataUnits(); + } + + class Block + { + public: + Block(BlockAllocator *a) : + m_a(a), + m_block(0), + m_firstFreeUnit(data_t(-1)), + m_allocated(0), + m_end(0) + {} + + ~Block() { + delete[] m_block; + } + + bool isFull() const { + return m_allocated == m_a->elementsPerBlock(); + } + + void clear() { + delete[] m_block; + m_block = 0; + m_firstFreeUnit = data_t(-1); + } + + void *allocate(data_t index) { + + if (m_firstFreeUnit == data_t(-1)) { + + if (!m_block) { + m_block = new data_t[m_a->blockSizeInDataUnits()]; + m_end = 0; + } + + data_t *retval = m_block + m_end; + m_end += m_a->unitSizeInDataUnits(); + retval[m_a->elementSizeInDataUnits()] = index; + ++m_allocated; + return retval; + + } else { + + data_t *retval = m_block + m_firstFreeUnit; + m_firstFreeUnit = *retval; + ++m_allocated; + return retval; + } + } + + void deallocate(data_t *ptr) { + + *ptr = m_firstFreeUnit; + m_firstFreeUnit = ptr - m_block; + + if (--m_allocated == 0) clear(); + } + + private: + const BlockAllocator *m_a; + data_t *m_block; + data_t m_firstFreeUnit; + data_t m_allocated; + data_t m_end; + }; + + struct Blocks + { + std::vector<Block> data; + + Blocks() { + data.reserve(1024); + } + }; + + const int m_sz; + Blocks m_blocks; + std::vector<data_t> m_freelist; +}; + +#endif diff --git a/libs/qm-dsp/thread/Thread.cpp b/libs/qm-dsp/thread/Thread.cpp new file mode 100644 index 0000000000..85dcec3155 --- /dev/null +++ b/libs/qm-dsp/thread/Thread.cpp @@ -0,0 +1,643 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + QM DSP Library + + Centre for Digital Music, Queen Mary, University of London. + This file copyright Chris Cannam, used with permission. +*/ + +#include "Thread.h" + +#include <iostream> +#include <cstdlib> + +#ifdef USE_PTHREADS +#include <sys/time.h> +#include <time.h> +#endif + +using std::cerr; +using std::endl; +using std::string; + +#ifdef _WIN32 + +Thread::Thread() : + m_id(0), + m_extant(false) +{ +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Created thread object " << this << endl; +#endif +} + +Thread::~Thread() +{ +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl; +#endif + if (m_extant) { + WaitForSingleObject(m_id, INFINITE); + } +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Destroyed thread object " << this << endl; +#endif +} + +void +Thread::start() +{ + m_id = CreateThread(NULL, 0, staticRun, this, 0, 0); + if (!m_id) { + cerr << "ERROR: thread creation failed" << endl; + exit(1); + } else { +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl; +#endif + m_extant = true; + } +} + +void +Thread::wait() +{ + if (m_extant) { +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl; +#endif + WaitForSingleObject(m_id, INFINITE); +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl; +#endif + m_extant = false; + } +} + +Thread::Id +Thread::id() +{ + return m_id; +} + +bool +Thread::threadingAvailable() +{ + return true; +} + +DWORD +Thread::staticRun(LPVOID arg) +{ + Thread *thread = static_cast<Thread *>(arg); +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: " << (void *)GetCurrentThreadId() << ": Running thread " << thread->m_id << " for thread object " << thread << endl; +#endif + thread->run(); + return 0; +} + +Mutex::Mutex() +#ifndef NO_THREAD_CHECKS + : + m_lockedBy(-1) +#endif +{ + m_mutex = CreateMutex(NULL, FALSE, NULL); +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised mutex " << &m_mutex << endl; +#endif +} + +Mutex::~Mutex() +{ +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying mutex " << &m_mutex << endl; +#endif + CloseHandle(m_mutex); +} + +void +Mutex::lock() +{ +#ifndef NO_THREAD_CHECKS + DWORD tid = GetCurrentThreadId(); + if (m_lockedBy == tid) { + cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl; + } +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; +#endif + WaitForSingleObject(m_mutex, INFINITE); +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; +#endif +} + +void +Mutex::unlock() +{ +#ifndef NO_THREAD_CHECKS + DWORD tid = GetCurrentThreadId(); + if (m_lockedBy != tid) { + cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl; + return; + } +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl; +#endif +#ifndef NO_THREAD_CHECKS + m_lockedBy = -1; +#endif + ReleaseMutex(m_mutex); +} + +bool +Mutex::trylock() +{ +#ifndef NO_THREAD_CHECKS + DWORD tid = GetCurrentThreadId(); +#endif + DWORD result = WaitForSingleObject(m_mutex, 0); + if (result == WAIT_TIMEOUT || result == WAIT_FAILED) { +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; +#endif + return false; + } else { +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; +#endif + return true; + } +} + +Condition::Condition(string name) : + m_locked(false) +#ifdef DEBUG_CONDITION + , m_name(name) +#endif +{ + m_mutex = CreateMutex(NULL, FALSE, NULL); + m_condition = CreateEvent(NULL, FALSE, FALSE, NULL); +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl; +#endif +} + +Condition::~Condition() +{ +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + if (m_locked) ReleaseMutex(m_mutex); + CloseHandle(m_condition); + CloseHandle(m_mutex); +} + +void +Condition::lock() +{ +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + WaitForSingleObject(m_mutex, INFINITE); + m_locked = true; +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl; +#endif +} + +void +Condition::unlock() +{ + if (!m_locked) { +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + return; + } +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + m_locked = false; + ReleaseMutex(m_mutex); +} + +void +Condition::wait(int us) +{ + if (us == 0) { + +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + SignalObjectAndWait(m_mutex, m_condition, INFINITE, FALSE); + WaitForSingleObject(m_mutex, INFINITE); + + } else { + + DWORD ms = us / 1000; + if (us > 0 && ms == 0) ms = 1; + +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + SignalObjectAndWait(m_mutex, m_condition, ms, FALSE); + WaitForSingleObject(m_mutex, INFINITE); + } + +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + + m_locked = true; +} + +void +Condition::signal() +{ +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + SetEvent(m_condition); +} + +#else /* !_WIN32 */ + +#ifdef USE_PTHREADS + +Thread::Thread() : + m_id(0), + m_extant(false) +{ +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Created thread object " << this << endl; +#endif +} + +Thread::~Thread() +{ +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl; +#endif + if (m_extant) { + pthread_join(m_id, 0); + } +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Destroyed thread object " << this << endl; +#endif +} + +void +Thread::start() +{ + if (pthread_create(&m_id, 0, staticRun, this)) { + cerr << "ERROR: thread creation failed" << endl; + exit(1); + } else { +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl; +#endif + m_extant = true; + } +} + +void +Thread::wait() +{ + if (m_extant) { +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl; +#endif + pthread_join(m_id, 0); +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl; +#endif + m_extant = false; + } +} + +Thread::Id +Thread::id() +{ + return m_id; +} + +bool +Thread::threadingAvailable() +{ + return true; +} + +void * +Thread::staticRun(void *arg) +{ + Thread *thread = static_cast<Thread *>(arg); +#ifdef DEBUG_THREAD + cerr << "THREAD DEBUG: " << (void *)pthread_self() << ": Running thread " << thread->m_id << " for thread object " << thread << endl; +#endif + thread->run(); + return 0; +} + +Mutex::Mutex() +#ifndef NO_THREAD_CHECKS + : + m_lockedBy(0), + m_locked(false) +#endif +{ + pthread_mutex_init(&m_mutex, 0); +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Initialised mutex " << &m_mutex << endl; +#endif +} + +Mutex::~Mutex() +{ +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Destroying mutex " << &m_mutex << endl; +#endif + pthread_mutex_destroy(&m_mutex); +} + +void +Mutex::lock() +{ +#ifndef NO_THREAD_CHECKS + pthread_t tid = pthread_self(); + if (m_locked && m_lockedBy == tid) { + cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl; + } +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; +#endif + pthread_mutex_lock(&m_mutex); +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; + m_locked = true; +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; +#endif +} + +void +Mutex::unlock() +{ +#ifndef NO_THREAD_CHECKS + pthread_t tid = pthread_self(); + if (!m_locked) { + cerr << "ERROR: Mutex " << &m_mutex << " not locked in unlock" << endl; + return; + } else if (m_lockedBy != tid) { + cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl; + return; + } +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl; +#endif +#ifndef NO_THREAD_CHECKS + m_locked = false; +#endif + pthread_mutex_unlock(&m_mutex); +} + +bool +Mutex::trylock() +{ +#ifndef NO_THREAD_CHECKS + pthread_t tid = pthread_self(); +#endif + if (pthread_mutex_trylock(&m_mutex)) { +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; +#endif + return false; + } else { +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; + m_locked = true; +#endif +#ifdef DEBUG_MUTEX + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; +#endif + return true; + } +} + +Condition::Condition(string name) : + m_locked(false) +#ifdef DEBUG_CONDITION + , m_name(name) +#endif +{ + pthread_mutex_init(&m_mutex, 0); + pthread_cond_init(&m_condition, 0); +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl; +#endif +} + +Condition::~Condition() +{ +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + if (m_locked) pthread_mutex_unlock(&m_mutex); + pthread_cond_destroy(&m_condition); + pthread_mutex_destroy(&m_mutex); +} + +void +Condition::lock() +{ +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + pthread_mutex_lock(&m_mutex); + m_locked = true; +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl; +#endif +} + +void +Condition::unlock() +{ + if (!m_locked) { +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + return; + } +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + m_locked = false; + pthread_mutex_unlock(&m_mutex); +} + +void +Condition::wait(int us) +{ + if (us == 0) { + +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + pthread_cond_wait(&m_condition, &m_mutex); + + } else { + + struct timeval now; + gettimeofday(&now, 0); + + now.tv_usec += us; + while (now.tv_usec > 1000000) { + now.tv_usec -= 1000000; + ++now.tv_sec; + } + + struct timespec timeout; + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = now.tv_usec * 1000; + +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + pthread_cond_timedwait(&m_condition, &m_mutex, &timeout); + } + +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + + m_locked = true; +} + +void +Condition::signal() +{ +#ifdef DEBUG_CONDITION + cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl; +#endif + pthread_cond_signal(&m_condition); +} + +#else /* !USE_PTHREADS */ + +Thread::Thread() +{ +} + +Thread::~Thread() +{ +} + +void +Thread::start() +{ + abort(); +} + +void +Thread::wait() +{ + abort(); +} + +Thread::Id +Thread::id() +{ + abort(); +} + +bool +Thread::threadingAvailable() +{ + return false; +} + +Mutex::Mutex() +{ +} + +Mutex::~Mutex() +{ +} + +void +Mutex::lock() +{ + abort(); +} + +void +Mutex::unlock() +{ + abort(); +} + +bool +Mutex::trylock() +{ + abort(); +} + +Condition::Condition(const char *) +{ +} + +Condition::~Condition() +{ +} + +void +Condition::lock() +{ + abort(); +} + +void +Condition::wait(int us) +{ + abort(); +} + +void +Condition::signal() +{ + abort(); +} + +#endif /* !USE_PTHREADS */ +#endif /* !_WIN32 */ + +MutexLocker::MutexLocker(Mutex *mutex) : + m_mutex(mutex) +{ + if (m_mutex) { + m_mutex->lock(); + } +} + +MutexLocker::~MutexLocker() +{ + if (m_mutex) { + m_mutex->unlock(); + } +} + diff --git a/libs/qm-dsp/thread/Thread.h b/libs/qm-dsp/thread/Thread.h new file mode 100644 index 0000000000..5e7c5b108f --- /dev/null +++ b/libs/qm-dsp/thread/Thread.h @@ -0,0 +1,149 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + QM DSP Library + + Centre for Digital Music, Queen Mary, University of London. + This file copyright Chris Cannam, used with permission. +*/ + +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#ifdef _WIN32 +#include <windows.h> +#else /* !_WIN32 */ +#ifdef USE_PTHREADS +#include <pthread.h> +#endif /* USE_PTHREADS */ +#endif /* !_WIN32 */ + +#include <string> + +//#define DEBUG_THREAD 1 +//#define DEBUG_MUTEX 1 +//#define DEBUG_CONDITION 1 + +class Thread +{ +public: +#ifdef _WIN32 + typedef HANDLE Id; +#else +#ifdef USE_PTHREADS + typedef pthread_t Id; +#endif +#endif + + Thread(); + virtual ~Thread(); + + Id id(); + + void start(); + void wait(); + + static bool threadingAvailable(); + +protected: + virtual void run() = 0; + +private: +#ifdef _WIN32 + HANDLE m_id; + bool m_extant; + static DWORD WINAPI staticRun(LPVOID lpParam); +#else +#ifdef USE_PTHREADS + pthread_t m_id; + bool m_extant; + static void *staticRun(void *); +#endif +#endif +}; + +class Mutex +{ +public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + bool trylock(); + +private: +#ifdef _WIN32 + HANDLE m_mutex; +#ifndef NO_THREAD_CHECKS + DWORD m_lockedBy; +#endif +#else +#ifdef USE_PTHREADS + pthread_mutex_t m_mutex; +#ifndef NO_THREAD_CHECKS + pthread_t m_lockedBy; + bool m_locked; +#endif +#endif +#endif +}; + +class MutexLocker +{ +public: + MutexLocker(Mutex *); + ~MutexLocker(); + +private: + Mutex *m_mutex; +}; + +class Condition +{ +public: + Condition(std::string name); + ~Condition(); + + // Condition bundles a pthread-style condition variable and mutex + // into one class. + + // To wait on a condition, call lock(), test termination variables + // as appropriate, and then wait(). The condition will be + // unlocked for the duration of the wait() call, which will end + // when the condition is signalled. The condition will be locked + // again when wait() returns. + // + // To signal a condition, call signal(). If the waiting thread + // will be performing tests between its own lock() and wait(), + // then the signalling thread should also lock() before it signals + // (and then unlock afterwards). If the signalling thread always + // locks the mutex during signalling, then the waiting thread + // knows that signals will only happen during wait() and not be + // missed at other times. + + void lock(); + void unlock(); + void wait(int us = 0); + + void signal(); + +private: + +#ifdef _WIN32 + HANDLE m_mutex; + HANDLE m_condition; + bool m_locked; +#else +#ifdef USE_PTHREADS + pthread_mutex_t m_mutex; + pthread_cond_t m_condition; + bool m_locked; +#endif +#endif +#ifdef DEBUG_CONDITION + std::string m_name; +#endif +}; + +#endif |