diff thread/Thread.cpp @ 62:b63f1ccbc9b6

* oops... add thread abstraction
author cannam
date Tue, 12 May 2009 17:56:58 +0000
parents
children 6afa0e011f74
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/Thread.cpp	Tue May 12 17:56:58 2009 +0000
@@ -0,0 +1,651 @@
+/* -*- 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()
+{
+    if (m_locked) {
+#ifdef DEBUG_CONDITION
+        cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Already locked " << &m_condition << " \"" << m_name << "\"" << endl;
+#endif
+        return;
+    }
+#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
+}
+
+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()
+{
+    if (m_locked) {
+#ifdef DEBUG_CONDITION
+        cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Already locked " << &m_condition << " \"" << m_name << "\"" << endl;
+#endif
+        return;
+    }
+#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
+}
+
+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();
+    }
+}
+