c@292: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@292: c@292: /* c@292: QM DSP Library c@292: c@292: Centre for Digital Music, Queen Mary, University of London. c@292: This file Copyright 2009 QMUL. c@292: */ c@292: c@292: #ifndef _ASYNCHRONOUS_TASK_H_ c@292: #define _ASYNCHRONOUS_TASK_H_ c@292: c@292: #include "Thread.h" c@292: c@294: #include c@294: c@292: /** c@292: * AsynchronousTask provides a thread pattern implementation for c@292: * threads which are used to perform a series of similar operations in c@292: * parallel with other threads of the same type. c@292: * c@292: * For example, a thread used to calculate FFTs of a particular block c@292: * size in the context of a class that needs to calculate many block c@292: * sizes of FFT at once may be a candidate for an AsynchronousTask. c@292: * c@292: * The general use pattern is: c@292: * c@292: * caller -> request thread A calculate something c@292: * caller -> request thread B calculate something c@292: * caller -> request thread C calculate something c@292: * caller -> wait for threads A, B, and C c@292: * c@292: * Here threads A, B, and C may be AsynchronousTasks. An important c@292: * point is that the caller must be prepared to block when waiting for c@292: * these threads to complete (i.e. they are started asynchronously, c@292: * but testing for completion is synchronous). c@292: */ c@292: class AsynchronousTask : public Thread c@292: { c@292: public: c@292: AsynchronousTask() : c@292: m_todo("AsynchronousTask: task to perform"), c@292: m_done("AsynchronousTask: task complete"), c@292: m_inTask(false), c@292: m_finishing(false) c@292: { c@292: start(); c@292: } c@292: virtual ~AsynchronousTask() c@292: { c@294: m_todo.lock(); c@292: m_finishing = true; c@292: m_todo.signal(); c@294: m_todo.unlock(); c@292: wait(); c@292: } c@292: c@292: // Subclass must provide methods to request task and obtain c@292: // results, which the caller calls. The method that requests a c@292: // new task should set up any internal state and call startTask(), c@292: // which then calls back on the subclass implementation of c@292: // performTask from within its work thread. The method that c@292: // obtains results should call awaitTask() and then return any c@292: // results from internal state. c@292: c@292: protected: c@292: void startTask() { c@294: m_done.lock(); c@292: m_todo.lock(); c@292: m_inTask = true; c@292: m_todo.signal(); c@292: m_todo.unlock(); c@292: } c@292: void awaitTask() { c@294: m_done.wait(); c@292: m_done.unlock(); c@292: } c@292: c@292: virtual void performTask() = 0; c@292: c@292: private: c@292: virtual void run() { c@292: m_todo.lock(); c@294: while (1) { c@292: while (!m_inTask && !m_finishing) { c@292: m_todo.wait(); c@292: } c@292: if (m_finishing) { c@294: m_done.lock(); c@293: m_inTask = false; c@293: m_done.signal(); c@294: m_done.unlock(); c@292: break; c@292: } c@294: performTask(); c@294: m_done.lock(); c@294: m_inTask = false; c@294: m_done.signal(); c@294: m_done.unlock(); c@292: } c@292: m_todo.unlock(); c@292: } c@292: c@292: Condition m_todo; c@292: Condition m_done; c@292: bool m_inTask; c@292: bool m_finishing; c@292: }; c@292: c@292: #endif