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.
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 #define _WIN32_WINNT	0x0400
39 #include "helperthread.h"
40 
41 //==========================================================================
42 //
43 // ---Constructor---
44 //
45 //==========================================================================
46 
FHelperThread()47 FHelperThread::FHelperThread ()
48 {
49 	ThreadHandle = NULL;
50 	ThreadID = 0;
51 	Thread_Events[0] = Thread_Events[1] = 0;
52 	memset (Messages, 0, sizeof(Messages));
53 	MessageHead = 0;
54 	MessageTail = 0;
55 }
56 
57 //==========================================================================
58 //
59 // ---Destructor---
60 //
61 //==========================================================================
62 
~FHelperThread()63 FHelperThread::~FHelperThread ()
64 {
65 	DestroyThread ();
66 }
67 
68 //==========================================================================
69 //
70 // LaunchThread
71 //
72 //==========================================================================
73 
LaunchThread()74 bool FHelperThread::LaunchThread ()
75 {
76 	int i;
77 
78 	MessageHead = MessageTail = 0;
79 	for (i = 0; i < MSG_QUEUE_SIZE; i++)
80 	{
81 		if ((Messages[i].CompletionEvent = CreateEvent (NULL, FALSE, i > 0, NULL)) == NULL)
82 			break;
83 	}
84 	if (i < MSG_QUEUE_SIZE)
85 	{
86 		for (; i >= 0; i--)
87 		{
88 			CloseHandle (Messages[i].CompletionEvent);
89 		}
90 		return false;
91 	}
92 	InitializeCriticalSection (&Thread_Critical);
93 	if ((Thread_Events[0] = CreateEvent (NULL, TRUE, FALSE, NULL)))
94 	{
95 		if ((Thread_Events[1] = CreateEvent (NULL, TRUE, FALSE, NULL)))
96 		{
97 			if ((ThreadHandle = CreateThread (NULL, 0, ThreadLaunch, (LPVOID)this, 0, &ThreadID)))
98 			{
99 				HANDLE waiters[2] = { Messages[0].CompletionEvent, ThreadHandle };
100 
101 				if (WaitForMultipleObjects (2, waiters, FALSE, INFINITE) == WAIT_OBJECT_0+1)
102 				{ // Init failed, and the thread exited
103 					DestroyThread ();
104 					return false;
105 				}
106 #if defined(_MSC_VER) && defined(_DEBUG)
107 				// Give the thread a name in the debugger
108 				struct
109 				{
110 					DWORD type;
111 					LPCSTR name;
112 					DWORD threadID;
113 					DWORD flags;
114 				} info =
115 				{
116 					0x1000, ThreadName(), ThreadID, 0
117 				};
118 				__try
119 				{
120 					RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR *)&info );
121 				}
122 				__except(EXCEPTION_CONTINUE_EXECUTION)
123 				{
124 				}
125 #endif
126 
127 				return true;
128 			}
129 			CloseHandle (Thread_Events[1]);
130 		}
131 		CloseHandle (Thread_Events[0]);
132 	}
133 	DeleteCriticalSection (&Thread_Critical);
134 	for (i = 0; i < MSG_QUEUE_SIZE; i++)
135 	{
136 		CloseHandle (Messages[i].CompletionEvent);
137 	}
138 	return false;
139 }
140 
141 //==========================================================================
142 //
143 // DestroyThread
144 //
145 //==========================================================================
146 
DestroyThread()147 void FHelperThread::DestroyThread ()
148 {
149 	int i;
150 
151 	if (ThreadHandle == NULL)
152 		return;
153 
154 	SetEvent (Thread_Events[THREAD_KillSelf]);
155 	// 5 seconds should be sufficient. If not, then we'll crash, but at
156 	// least that's better than hanging indefinitely.
157 	WaitForSingleObject (ThreadHandle, 5000);
158 	CloseHandle (ThreadHandle);
159 
160 	EnterCriticalSection (&Thread_Critical);
161 
162 	for (i = 0; i < 8; i++)
163 	{
164 		CloseHandle (Messages[i].CompletionEvent);
165 	}
166 	CloseHandle (Thread_Events[0]);
167 	CloseHandle (Thread_Events[1]);
168 
169 	LeaveCriticalSection (&Thread_Critical);
170 	DeleteCriticalSection (&Thread_Critical);
171 
172 	ThreadHandle = NULL;
173 }
174 
175 //==========================================================================
176 //
177 // HaveThread
178 //
179 //==========================================================================
180 
HaveThread() const181 bool FHelperThread::HaveThread () const
182 {
183 	return ThreadHandle != NULL;
184 }
185 
186 //==========================================================================
187 //
188 // SendMessage
189 //
190 //==========================================================================
191 
SendMessage(DWORD method,DWORD parm1,DWORD parm2,DWORD parm3,bool wait)192 DWORD FHelperThread::SendMessage (DWORD method,
193 	DWORD parm1, DWORD parm2, DWORD parm3, bool wait)
194 {
195 	for (;;)
196 	{
197 		EnterCriticalSection (&Thread_Critical);
198 		if (MessageHead - MessageTail == MSG_QUEUE_SIZE)
199 		{ // No room, so wait for oldest message to complete
200 			HANDLE waitEvent = Messages[MessageTail].CompletionEvent;
201 			LeaveCriticalSection (&Thread_Critical);
202 			WaitForSingleObject (waitEvent, 1000);
203 		}
204 
205 		int head = MessageHead++ % MSG_QUEUE_SIZE;
206 		Messages[head].Method = method;
207 		Messages[head].Parm1 = parm1;
208 		Messages[head].Parm2 = parm2;
209 		Messages[head].Parm3 = parm3;
210 		ResetEvent (Messages[head].CompletionEvent);
211 		LeaveCriticalSection (&Thread_Critical);
212 
213 		SetEvent (Thread_Events[THREAD_NewMessage]);
214 
215 		if (wait)
216 		{
217 			WaitForSingleObject (Messages[head].CompletionEvent, INFINITE);
218 			return Messages[head].Return;
219 		}
220 		return 0;
221 	}
222 }
223 
224 //==========================================================================
225 //
226 // ThreadLaunch (static)
227 //
228 //==========================================================================
229 
ThreadLaunch(LPVOID me)230 DWORD WINAPI FHelperThread::ThreadLaunch (LPVOID me)
231 {
232 	return ((FHelperThread *)me)->ThreadLoop ();
233 }
234 
235 //==========================================================================
236 //
237 // ThreadLoop
238 //
239 //==========================================================================
240 
ThreadLoop()241 DWORD FHelperThread::ThreadLoop ()
242 {
243 	if (!Init ())
244 	{
245 		ExitThread (0);
246 	}
247 	else
248 	{
249 		SetEvent (Messages[0].CompletionEvent);
250 	}
251 
252 	for (;;)
253 	{
254 		switch (MsgWaitForMultipleObjects (2, Thread_Events,
255 			FALSE, INFINITE, QS_ALLEVENTS))
256 		{
257 		case WAIT_OBJECT_0+1:
258 			// We should quit now.
259 			Deinit ();
260 			ExitThread (0);
261 			break;
262 
263 		case WAIT_OBJECT_0:
264 			// Process any new messages. MessageTail is not updated until *after*
265 			// the message is finished processing.
266 			for (;;)
267 			{
268 				EnterCriticalSection (&Thread_Critical);
269 				if (MessageHead == MessageTail)
270 				{
271 					ResetEvent (Thread_Events[0]);
272 					LeaveCriticalSection (&Thread_Critical);
273 					break;	// Resume outer for (Wait for more messages)
274 				}
275 				int spot = MessageTail % MSG_QUEUE_SIZE;
276 				LeaveCriticalSection (&Thread_Critical);
277 
278 				Messages[spot].Return = Dispatch (Messages[spot].Method,
279 					Messages[spot].Parm1, Messages[spot].Parm2,
280 					Messages[spot].Parm3);
281 
282 				// No need to enter critical section, because only the CD thread
283 				// is allowed to touch MessageTail or signal the CompletionEvent
284 				SetEvent (Messages[spot].CompletionEvent);
285 				MessageTail++;
286 			}
287 			break;
288 
289 		default:
290 			DefaultDispatch ();
291 		}
292 	}
293 }
294