111 lines
2.9 KiB
C++
111 lines
2.9 KiB
C++
/* -*- 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
|