add queen mary DSP library
[ardour.git] / libs / qm-dsp / thread / AsynchronousTask.h
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
3 /*
4     QM DSP Library
5
6     Centre for Digital Music, Queen Mary, University of London.
7     This file Copyright 2009 QMUL.
8 */
9
10 #ifndef _ASYNCHRONOUS_TASK_H_
11 #define _ASYNCHRONOUS_TASK_H_
12
13 #include "Thread.h"
14
15 #include <iostream>
16
17 /**
18  * AsynchronousTask provides a thread pattern implementation for
19  * threads which are used to perform a series of similar operations in
20  * parallel with other threads of the same type.
21  *
22  * For example, a thread used to calculate FFTs of a particular block
23  * size in the context of a class that needs to calculate many block
24  * sizes of FFT at once may be a candidate for an AsynchronousTask.
25  *
26  * The general use pattern is:
27  *
28  *   caller -> request thread A calculate something
29  *   caller -> request thread B calculate something
30  *   caller -> request thread C calculate something
31  *   caller -> wait for threads A, B, and C
32  *
33  * Here threads A, B, and C may be AsynchronousTasks.  An important
34  * point is that the caller must be prepared to block when waiting for
35  * these threads to complete (i.e. they are started asynchronously,
36  * but testing for completion is synchronous).
37  */
38 class AsynchronousTask : public Thread
39 {
40 public:
41     AsynchronousTask() :
42         m_todo("AsynchronousTask: task to perform"),
43         m_done("AsynchronousTask: task complete"),
44         m_inTask(false),
45         m_finishing(false)
46     {
47         start();
48     }
49     virtual ~AsynchronousTask()
50     {
51         m_todo.lock();
52         m_finishing = true;
53         m_todo.signal();
54         m_todo.unlock();
55         wait();
56     }
57
58     // Subclass must provide methods to request task and obtain
59     // results, which the caller calls.  The method that requests a
60     // new task should set up any internal state and call startTask(),
61     // which then calls back on the subclass implementation of
62     // performTask from within its work thread.  The method that
63     // obtains results should call awaitTask() and then return any
64     // results from internal state.
65
66 protected:
67     void startTask() {
68         m_done.lock();
69         m_todo.lock();
70         m_inTask = true;
71         m_todo.signal();
72         m_todo.unlock();
73     }
74     void awaitTask() {
75         m_done.wait();
76         m_done.unlock();
77     }
78
79     virtual void performTask() = 0;
80     
81 private:
82     virtual void run() {
83         m_todo.lock();
84         while (1) {
85             while (!m_inTask && !m_finishing) {
86                 m_todo.wait();
87             }
88             if (m_finishing) {
89                 m_done.lock();
90                 m_inTask = false;
91                 m_done.signal();
92                 m_done.unlock();
93                 break;
94             }
95             performTask();
96             m_done.lock();
97             m_inTask = false;
98             m_done.signal();
99             m_done.unlock();
100         }
101         m_todo.unlock();
102     }
103
104     Condition m_todo;
105     Condition m_done;
106     bool m_inTask;
107     bool m_finishing;
108 };
109
110 #endif