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