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