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