summaryrefslogtreecommitdiff
path: root/libs/qm-dsp/thread
diff options
context:
space:
mode:
Diffstat (limited to 'libs/qm-dsp/thread')
-rw-r--r--libs/qm-dsp/thread/AsynchronousTask.h110
-rw-r--r--libs/qm-dsp/thread/BlockAllocator.h189
-rw-r--r--libs/qm-dsp/thread/Thread.cpp643
-rw-r--r--libs/qm-dsp/thread/Thread.h149
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