cannam@62
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@62
|
2
|
cannam@62
|
3 /*
|
cannam@62
|
4 QM DSP Library
|
cannam@62
|
5
|
cannam@62
|
6 Centre for Digital Music, Queen Mary, University of London.
|
cannam@62
|
7 This file copyright Chris Cannam, used with permission.
|
cannam@62
|
8 */
|
cannam@62
|
9
|
cannam@62
|
10 #ifndef _THREAD_H_
|
cannam@62
|
11 #define _THREAD_H_
|
cannam@62
|
12
|
cannam@62
|
13 #ifdef _WIN32
|
cannam@62
|
14 #include <windows.h>
|
cannam@62
|
15 #else /* !_WIN32 */
|
cannam@62
|
16 #ifdef USE_PTHREADS
|
cannam@62
|
17 #include <pthread.h>
|
cannam@62
|
18 #endif /* USE_PTHREADS */
|
cannam@62
|
19 #endif /* !_WIN32 */
|
cannam@62
|
20
|
cannam@62
|
21 #include <string>
|
cannam@62
|
22
|
cannam@62
|
23 //#define DEBUG_THREAD 1
|
cannam@62
|
24 //#define DEBUG_MUTEX 1
|
cannam@62
|
25 //#define DEBUG_CONDITION 1
|
cannam@62
|
26
|
cannam@62
|
27 class Thread
|
cannam@62
|
28 {
|
cannam@62
|
29 public:
|
cannam@62
|
30 #ifdef _WIN32
|
cannam@62
|
31 typedef HANDLE Id;
|
cannam@62
|
32 #else
|
cannam@62
|
33 #ifdef USE_PTHREADS
|
cannam@62
|
34 typedef pthread_t Id;
|
cannam@62
|
35 #endif
|
cannam@62
|
36 #endif
|
cannam@62
|
37
|
cannam@62
|
38 Thread();
|
cannam@62
|
39 virtual ~Thread();
|
cannam@62
|
40
|
cannam@62
|
41 Id id();
|
cannam@62
|
42
|
cannam@62
|
43 void start();
|
cannam@62
|
44 void wait();
|
cannam@62
|
45
|
cannam@62
|
46 static bool threadingAvailable();
|
cannam@62
|
47
|
cannam@62
|
48 protected:
|
cannam@62
|
49 virtual void run() = 0;
|
cannam@62
|
50
|
cannam@62
|
51 private:
|
cannam@62
|
52 #ifdef _WIN32
|
cannam@62
|
53 HANDLE m_id;
|
cannam@62
|
54 bool m_extant;
|
cannam@62
|
55 static DWORD WINAPI staticRun(LPVOID lpParam);
|
cannam@62
|
56 #else
|
cannam@62
|
57 #ifdef USE_PTHREADS
|
cannam@62
|
58 pthread_t m_id;
|
cannam@62
|
59 bool m_extant;
|
cannam@62
|
60 static void *staticRun(void *);
|
cannam@62
|
61 #endif
|
cannam@62
|
62 #endif
|
cannam@62
|
63 };
|
cannam@62
|
64
|
cannam@62
|
65 class Mutex
|
cannam@62
|
66 {
|
cannam@62
|
67 public:
|
cannam@62
|
68 Mutex();
|
cannam@62
|
69 ~Mutex();
|
cannam@62
|
70
|
cannam@62
|
71 void lock();
|
cannam@62
|
72 void unlock();
|
cannam@62
|
73 bool trylock();
|
cannam@62
|
74
|
cannam@62
|
75 private:
|
cannam@62
|
76 #ifdef _WIN32
|
cannam@62
|
77 HANDLE m_mutex;
|
cannam@62
|
78 #ifndef NO_THREAD_CHECKS
|
cannam@62
|
79 DWORD m_lockedBy;
|
cannam@62
|
80 #endif
|
cannam@62
|
81 #else
|
cannam@62
|
82 #ifdef USE_PTHREADS
|
cannam@62
|
83 pthread_mutex_t m_mutex;
|
cannam@62
|
84 #ifndef NO_THREAD_CHECKS
|
cannam@62
|
85 pthread_t m_lockedBy;
|
cannam@62
|
86 bool m_locked;
|
cannam@62
|
87 #endif
|
cannam@62
|
88 #endif
|
cannam@62
|
89 #endif
|
cannam@62
|
90 };
|
cannam@62
|
91
|
cannam@62
|
92 class MutexLocker
|
cannam@62
|
93 {
|
cannam@62
|
94 public:
|
cannam@62
|
95 MutexLocker(Mutex *);
|
cannam@62
|
96 ~MutexLocker();
|
cannam@62
|
97
|
cannam@62
|
98 private:
|
cannam@62
|
99 Mutex *m_mutex;
|
cannam@62
|
100 };
|
cannam@62
|
101
|
cannam@62
|
102 class Condition
|
cannam@62
|
103 {
|
cannam@62
|
104 public:
|
cannam@62
|
105 Condition(std::string name);
|
cannam@62
|
106 ~Condition();
|
cannam@62
|
107
|
cannam@63
|
108 // Condition bundles a pthread-style condition variable and mutex
|
cannam@63
|
109 // into one class.
|
cannam@62
|
110
|
cannam@63
|
111 // To wait on a condition, call lock(), test termination variables
|
cannam@63
|
112 // as appropriate, and then wait(). The condition will be
|
cannam@63
|
113 // unlocked for the duration of the wait() call, which will end
|
cannam@63
|
114 // when the condition is signalled. The condition will be locked
|
cannam@63
|
115 // again when wait() returns.
|
cannam@63
|
116 //
|
cannam@63
|
117 // To signal a condition, call signal(). If the waiting thread
|
cannam@63
|
118 // will be performing tests between its own lock() and wait(),
|
cannam@63
|
119 // then the signalling thread should also lock() before it signals
|
cannam@63
|
120 // (and then unlock afterwards). If the signalling thread always
|
cannam@63
|
121 // locks the mutex during signalling, then the waiting thread
|
cannam@63
|
122 // knows that signals will only happen during wait() and not be
|
cannam@63
|
123 // missed at other times.
|
cannam@63
|
124
|
cannam@62
|
125 void lock();
|
cannam@62
|
126 void unlock();
|
cannam@62
|
127 void wait(int us = 0);
|
cannam@62
|
128
|
cannam@62
|
129 void signal();
|
cannam@62
|
130
|
cannam@62
|
131 private:
|
cannam@62
|
132
|
cannam@62
|
133 #ifdef _WIN32
|
cannam@62
|
134 HANDLE m_mutex;
|
cannam@62
|
135 HANDLE m_condition;
|
cannam@62
|
136 bool m_locked;
|
cannam@62
|
137 #else
|
cannam@62
|
138 #ifdef USE_PTHREADS
|
cannam@62
|
139 pthread_mutex_t m_mutex;
|
cannam@62
|
140 pthread_cond_t m_condition;
|
cannam@62
|
141 bool m_locked;
|
cannam@62
|
142 #endif
|
cannam@62
|
143 #endif
|
cannam@62
|
144 #ifdef DEBUG_CONDITION
|
cannam@62
|
145 std::string m_name;
|
cannam@62
|
146 #endif
|
cannam@62
|
147 };
|
cannam@62
|
148
|
cannam@63
|
149 class AsynchronousTask : public Thread
|
cannam@63
|
150 {
|
cannam@63
|
151 public:
|
cannam@63
|
152 AsynchronousTask() :
|
cannam@63
|
153 m_todo("AsynchronousTask: task to perform"),
|
cannam@63
|
154 m_done("AsynchronousTask: task complete"),
|
cannam@63
|
155 m_inTask(false),
|
cannam@63
|
156 m_finishing(false)
|
cannam@63
|
157 {
|
cannam@63
|
158 start();
|
cannam@63
|
159 }
|
cannam@63
|
160 virtual ~AsynchronousTask()
|
cannam@63
|
161 {
|
cannam@63
|
162 m_finishing = true;
|
cannam@63
|
163 m_todo.signal();
|
cannam@63
|
164 wait();
|
cannam@63
|
165 }
|
cannam@63
|
166
|
cannam@63
|
167 // subclass must provide methods to request task and obtain
|
cannam@63
|
168 // results
|
cannam@63
|
169
|
cannam@63
|
170 protected:
|
cannam@63
|
171 void startTask() {
|
cannam@63
|
172 m_todo.lock();
|
cannam@63
|
173 m_inTask = true;
|
cannam@63
|
174 m_todo.signal();
|
cannam@63
|
175 m_done.lock();
|
cannam@63
|
176 m_todo.unlock();
|
cannam@63
|
177 }
|
cannam@63
|
178 void awaitTask() {
|
cannam@63
|
179 while (m_inTask) m_done.wait();
|
cannam@63
|
180 m_done.unlock();
|
cannam@63
|
181 }
|
cannam@63
|
182
|
cannam@63
|
183 virtual void performTask() = 0;
|
cannam@63
|
184
|
cannam@63
|
185 private:
|
cannam@63
|
186 virtual void run() {
|
cannam@63
|
187 m_todo.lock();
|
cannam@63
|
188 while (!m_finishing) {
|
cannam@66
|
189 while (!m_inTask && !m_finishing) {
|
cannam@66
|
190 m_todo.wait();
|
cannam@66
|
191 }
|
cannam@66
|
192 if (m_finishing) {
|
cannam@66
|
193 break;
|
cannam@66
|
194 }
|
cannam@63
|
195 if (m_inTask) {
|
cannam@63
|
196 performTask();
|
cannam@63
|
197 m_inTask = false;
|
cannam@63
|
198 m_done.signal();
|
cannam@63
|
199 }
|
cannam@63
|
200 }
|
cannam@63
|
201 m_todo.unlock();
|
cannam@63
|
202 }
|
cannam@63
|
203
|
cannam@63
|
204 Condition m_todo;
|
cannam@63
|
205 Condition m_done;
|
cannam@63
|
206 bool m_inTask;
|
cannam@63
|
207 bool m_finishing;
|
cannam@63
|
208 };
|
cannam@63
|
209
|
cannam@62
|
210 #endif
|