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