view thread/Thread.h @ 66:2af6edd98dfa

* minor
author cannam
date Wed, 13 May 2009 17:41:10 +0000
parents 0dcbce5d7dce
children 516c86946900
line wrap: on
line source
/* -*- 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
};

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_finishing = true;
        m_todo.signal();
        wait();
    }

    // subclass must provide methods to request task and obtain
    // results

protected:
    void startTask() {
        m_todo.lock();
        m_inTask = true;
        m_todo.signal();
        m_done.lock();
        m_todo.unlock();
    }
    void awaitTask() {
        while (m_inTask) m_done.wait();
        m_done.unlock();
    }

    virtual void performTask() = 0;
    
private:
    virtual void run() {
        m_todo.lock();
        while (!m_finishing) {
            while (!m_inTask && !m_finishing) {
                m_todo.wait();
            }
            if (m_finishing) {
                break;
            }
            if (m_inTask) {
                performTask();
                m_inTask = false;
                m_done.signal();
            }
        }
        m_todo.unlock();
    }

    Condition m_todo;
    Condition m_done;
    bool m_inTask;
    bool m_finishing;
};

#endif