1 /*
2 ** helperthread.cpp
3 **
4 ** Implements FHelperThread, the base class for helper threads. Includes
5 ** a message queue for passing messages from the main thread to the
6 ** helper thread. (Only used by the CD Audio player)
7 **
8 **---------------------------------------------------------------------------
9 ** Copyright 1998-2006 Randy Heit
10 ** All rights reserved.
11 **
12 ** Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions
14 ** are met:
15 **
16 ** 1. Redistributions of source code must retain the above copyright
17 ** notice, this list of conditions and the following disclaimer.
18 ** 2. Redistributions in binary form must reproduce the above copyright
19 ** notice, this list of conditions and the following disclaimer in the
20 ** documentation and/or other materials provided with the distribution.
21 ** 3. The name of the author may not be used to endorse or promote products
22 ** derived from this software without specific prior written permission.
23 **
24 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 **---------------------------------------------------------------------------
35 **
36 */
37
38 #include "helperthread.h"
39
40 //==========================================================================
41 //
42 // ---Constructor---
43 //
44 //==========================================================================
45
FHelperThread()46 FHelperThread::FHelperThread ()
47 {
48 ThreadHandle = NULL;
49 ThreadID = 0;
50 Thread_Events[0] = Thread_Events[1] = 0;
51 memset (Messages, 0, sizeof(Messages));
52 MessageHead = 0;
53 MessageTail = 0;
54 }
55
56 //==========================================================================
57 //
58 // ---Destructor---
59 //
60 //==========================================================================
61
~FHelperThread()62 FHelperThread::~FHelperThread ()
63 {
64 DestroyThread ();
65 }
66
67 //==========================================================================
68 //
69 // LaunchThread
70 //
71 //==========================================================================
72
LaunchThread()73 bool FHelperThread::LaunchThread ()
74 {
75 int i;
76
77 MessageHead = MessageTail = 0;
78 for (i = 0; i < MSG_QUEUE_SIZE; i++)
79 {
80 if ((Messages[i].CompletionEvent = CreateEvent (NULL, FALSE, i > 0, NULL)) == NULL)
81 break;
82 }
83 if (i < MSG_QUEUE_SIZE)
84 {
85 for (; i >= 0; i--)
86 {
87 CloseHandle (Messages[i].CompletionEvent);
88 }
89 return false;
90 }
91 InitializeCriticalSection (&Thread_Critical);
92 if ((Thread_Events[0] = CreateEvent (NULL, TRUE, FALSE, NULL)))
93 {
94 if ((Thread_Events[1] = CreateEvent (NULL, TRUE, FALSE, NULL)))
95 {
96 if ((ThreadHandle = CreateThread (NULL, 0, ThreadLaunch, (LPVOID)this, 0, &ThreadID)))
97 {
98 HANDLE waiters[2] = { Messages[0].CompletionEvent, ThreadHandle };
99
100 if (WaitForMultipleObjects (2, waiters, FALSE, INFINITE) == WAIT_OBJECT_0+1)
101 { // Init failed, and the thread exited
102 DestroyThread ();
103 return false;
104 }
105 #if defined(_MSC_VER) && defined(_DEBUG)
106 // Give the thread a name in the debugger
107 struct
108 {
109 DWORD type;
110 LPCSTR name;
111 DWORD threadID;
112 DWORD flags;
113 } info =
114 {
115 0x1000, ThreadName(), ThreadID, 0
116 };
117 __try
118 {
119 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR *)&info );
120 }
121 __except(EXCEPTION_CONTINUE_EXECUTION)
122 {
123 }
124 #endif
125
126 return true;
127 }
128 CloseHandle (Thread_Events[1]);
129 }
130 CloseHandle (Thread_Events[0]);
131 }
132 DeleteCriticalSection (&Thread_Critical);
133 for (i = 0; i < MSG_QUEUE_SIZE; i++)
134 {
135 CloseHandle (Messages[i].CompletionEvent);
136 }
137 return false;
138 }
139
140 //==========================================================================
141 //
142 // DestroyThread
143 //
144 //==========================================================================
145
DestroyThread()146 void FHelperThread::DestroyThread ()
147 {
148 int i;
149
150 if (ThreadHandle == NULL)
151 return;
152
153 SetEvent (Thread_Events[THREAD_KillSelf]);
154 // 5 seconds should be sufficient. If not, then we'll crash, but at
155 // least that's better than hanging indefinitely.
156 WaitForSingleObject (ThreadHandle, 5000);
157 CloseHandle (ThreadHandle);
158
159 EnterCriticalSection (&Thread_Critical);
160
161 for (i = 0; i < 8; i++)
162 {
163 CloseHandle (Messages[i].CompletionEvent);
164 }
165 CloseHandle (Thread_Events[0]);
166 CloseHandle (Thread_Events[1]);
167
168 LeaveCriticalSection (&Thread_Critical);
169 DeleteCriticalSection (&Thread_Critical);
170
171 ThreadHandle = NULL;
172 }
173
174 //==========================================================================
175 //
176 // HaveThread
177 //
178 //==========================================================================
179
HaveThread() const180 bool FHelperThread::HaveThread () const
181 {
182 return ThreadHandle != NULL;
183 }
184
185 //==========================================================================
186 //
187 // SendMessage
188 //
189 //==========================================================================
190
SendMessage(DWORD method,DWORD parm1,DWORD parm2,DWORD parm3,bool wait)191 DWORD FHelperThread::SendMessage (DWORD method,
192 DWORD parm1, DWORD parm2, DWORD parm3, bool wait)
193 {
194 for (;;)
195 {
196 EnterCriticalSection (&Thread_Critical);
197 if (MessageHead - MessageTail == MSG_QUEUE_SIZE)
198 { // No room, so wait for oldest message to complete
199 HANDLE waitEvent = Messages[MessageTail].CompletionEvent;
200 LeaveCriticalSection (&Thread_Critical);
201 WaitForSingleObject (waitEvent, 1000);
202 }
203
204 int head = MessageHead++ % MSG_QUEUE_SIZE;
205 Messages[head].Method = method;
206 Messages[head].Parm1 = parm1;
207 Messages[head].Parm2 = parm2;
208 Messages[head].Parm3 = parm3;
209 ResetEvent (Messages[head].CompletionEvent);
210 LeaveCriticalSection (&Thread_Critical);
211
212 SetEvent (Thread_Events[THREAD_NewMessage]);
213
214 if (wait)
215 {
216 WaitForSingleObject (Messages[head].CompletionEvent, INFINITE);
217 return Messages[head].Return;
218 }
219 return 0;
220 }
221 }
222
223 //==========================================================================
224 //
225 // ThreadLaunch (static)
226 //
227 //==========================================================================
228
ThreadLaunch(LPVOID me)229 DWORD WINAPI FHelperThread::ThreadLaunch (LPVOID me)
230 {
231 return ((FHelperThread *)me)->ThreadLoop ();
232 }
233
234 //==========================================================================
235 //
236 // ThreadLoop
237 //
238 //==========================================================================
239
ThreadLoop()240 DWORD FHelperThread::ThreadLoop ()
241 {
242 if (!Init ())
243 {
244 ExitThread (0);
245 }
246 else
247 {
248 SetEvent (Messages[0].CompletionEvent);
249 }
250
251 for (;;)
252 {
253 switch (MsgWaitForMultipleObjects (2, Thread_Events,
254 FALSE, INFINITE, QS_ALLEVENTS))
255 {
256 case WAIT_OBJECT_0+1:
257 // We should quit now.
258 Deinit ();
259 ExitThread (0);
260 break;
261
262 case WAIT_OBJECT_0:
263 // Process any new messages. MessageTail is not updated until *after*
264 // the message is finished processing.
265 for (;;)
266 {
267 EnterCriticalSection (&Thread_Critical);
268 if (MessageHead == MessageTail)
269 {
270 ResetEvent (Thread_Events[0]);
271 LeaveCriticalSection (&Thread_Critical);
272 break; // Resume outer for (Wait for more messages)
273 }
274 int spot = MessageTail % MSG_QUEUE_SIZE;
275 LeaveCriticalSection (&Thread_Critical);
276
277 Messages[spot].Return = Dispatch (Messages[spot].Method,
278 Messages[spot].Parm1, Messages[spot].Parm2,
279 Messages[spot].Parm3);
280
281 // No need to enter critical section, because only the CD thread
282 // is allowed to touch MessageTail or signal the CompletionEvent
283 SetEvent (Messages[spot].CompletionEvent);
284 MessageTail++;
285 }
286 break;
287
288 default:
289 DefaultDispatch ();
290 }
291 }
292 }
293