1 /*
2 * PROJECT: ReactOS Sound System "MME Buddy" Library
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: lib/drivers/sound/mmebuddy/thread.c
5 *
6 * PURPOSE: Multimedia thread management
7 *
8 * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
9 */
10
11 #include "precomp.h"
12
13 DWORD WINAPI
SoundThreadMain(IN LPVOID lpParameter OPTIONAL)14 SoundThreadMain(
15 IN LPVOID lpParameter OPTIONAL)
16 {
17 PSOUND_THREAD Thread = (PSOUND_THREAD) lpParameter;
18
19 SND_TRACE(L"SoundThread running :)\n");
20
21 /* Callers will wait for us to be ready */
22 Thread->Running = TRUE;
23 SetEvent(Thread->Events.Ready);
24
25 while ( Thread->Running )
26 {
27 DWORD WaitResult;
28
29 /* Wait for a request, or an I/O completion */
30 WaitResult = WaitForSingleObjectEx(Thread->Events.Request, INFINITE, TRUE);
31 SND_TRACE(L"SoundThread - Came out of waiting\n");
32
33 if ( WaitResult == WAIT_OBJECT_0 )
34 {
35 SND_TRACE(L"SoundThread - Processing request\n");
36
37 if ( Thread->Request.Handler )
38 {
39 Thread->Request.Result = Thread->Request.Handler(Thread->Request.SoundDeviceInstance,
40 Thread->Request.Parameter);
41 }
42 else
43 {
44 Thread->Request.Result = MMSYSERR_ERROR;
45 }
46
47 /* Announce completion of the request */
48 SetEvent(Thread->Events.Done);
49 /* Accept new requests */
50 SetEvent(Thread->Events.Ready);
51 }
52 else if ( WaitResult == WAIT_IO_COMPLETION )
53 {
54 SND_TRACE(L"SoundThread - Processing IO completion\n");
55 /* TODO? What do we do here? Stream stuff? */
56 }
57 else
58 {
59 /* This should not happen! */
60 SND_ASSERT(FALSE);
61 }
62
63 }
64
65 SND_TRACE(L"Sound thread terminated\n");
66
67 return 0;
68 }
69
70 MMRESULT
CallSoundThread(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN SOUND_THREAD_REQUEST_HANDLER RequestHandler,IN PVOID Parameter OPTIONAL)71 CallSoundThread(
72 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
73 IN SOUND_THREAD_REQUEST_HANDLER RequestHandler,
74 IN PVOID Parameter OPTIONAL)
75 {
76 PSOUND_THREAD Thread;
77
78 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
79 VALIDATE_MMSYS_PARAMETER( RequestHandler );
80
81 Thread = SoundDeviceInstance->Thread;
82
83 SND_TRACE(L"Waiting for READY event\n");
84 WaitForSingleObject(Thread->Events.Ready, INFINITE);
85
86 Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
87 Thread->Request.Handler = RequestHandler;
88 Thread->Request.SoundDeviceInstance = SoundDeviceInstance;
89 Thread->Request.Parameter = Parameter;
90
91 /* Notify the thread it has work to do */
92 SND_TRACE(L"Setting REQUEST event\n");
93 SetEvent(Thread->Events.Request);
94
95 /* Wait for the work to be done */
96 SND_TRACE(L"Waiting for DONE event\n");
97 WaitForSingleObject(Thread->Events.Done, INFINITE);
98
99 return Thread->Request.Result;
100 }
101
102
103 MMRESULT
SoundThreadTerminator(IN PSOUND_DEVICE_INSTANCE Instance,IN PVOID Parameter)104 SoundThreadTerminator(
105 IN PSOUND_DEVICE_INSTANCE Instance,
106 IN PVOID Parameter)
107 {
108 PSOUND_THREAD Thread = (PSOUND_THREAD) Parameter;
109
110 SND_TRACE(L"Sound thread terminator routine called\n");
111 SND_ASSERT( Thread );
112
113 Thread->Running = FALSE;
114
115 return MMSYSERR_NOERROR;
116 }
117
118 MMRESULT
TerminateSoundThread(IN PSOUND_THREAD Thread)119 TerminateSoundThread(
120 IN PSOUND_THREAD Thread)
121 {
122 DWORD WaitResult;
123
124 SND_ASSERT( Thread );
125
126 SND_TRACE(L"Waiting for READY event\n");
127 WaitForSingleObject(Thread->Events.Ready, INFINITE);
128
129 Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
130 Thread->Request.Handler = SoundThreadTerminator;
131 Thread->Request.SoundDeviceInstance = NULL;
132 Thread->Request.Parameter = (PVOID) Thread;
133
134 /* Notify the thread it has work to do */
135 SND_TRACE(L"Setting REQUEST event\n");
136 SetEvent(Thread->Events.Request);
137
138 /* Wait for the work to be done */
139 SND_TRACE(L"Waiting for DONE event\n");
140 WaitForSingleObject(Thread->Events.Done, INFINITE);
141
142 /* Wait for the thread to actually end */
143 WaitResult = WaitForSingleObject(Thread->Handle, INFINITE);
144 SND_ASSERT( WaitResult == WAIT_OBJECT_0 );
145
146 return MMSYSERR_NOERROR;
147 }
148
149
150 MMRESULT
CreateSoundThreadEvents(OUT HANDLE * ReadyEvent,OUT HANDLE * RequestEvent,OUT HANDLE * DoneEvent)151 CreateSoundThreadEvents(
152 OUT HANDLE* ReadyEvent,
153 OUT HANDLE* RequestEvent,
154 OUT HANDLE* DoneEvent)
155 {
156 BOOL ok;
157
158 VALIDATE_MMSYS_PARAMETER( ReadyEvent );
159 VALIDATE_MMSYS_PARAMETER( RequestEvent );
160 VALIDATE_MMSYS_PARAMETER( DoneEvent );
161
162 SND_TRACE(L"Creating thread events\n");
163
164 /* Initialise these so we can identify them upon failure */
165 *ReadyEvent = *RequestEvent = *DoneEvent = INVALID_HANDLE_VALUE;
166
167 ok = (*ReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != INVALID_HANDLE_VALUE;
168 ok &= (*RequestEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != INVALID_HANDLE_VALUE;
169 ok &= (*DoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != INVALID_HANDLE_VALUE;
170
171 /* If something went wrong, clean up */
172 if ( ! ok )
173 {
174 if ( *ReadyEvent != INVALID_HANDLE_VALUE )
175 CloseHandle(*ReadyEvent);
176
177 if ( *RequestEvent != INVALID_HANDLE_VALUE )
178 CloseHandle(*RequestEvent);
179
180 if ( *DoneEvent != INVALID_HANDLE_VALUE )
181 CloseHandle(*DoneEvent);
182
183 return MMSYSERR_NOMEM;
184 }
185
186 return MMSYSERR_NOERROR;
187 }
188
189 MMRESULT
DestroySoundThreadEvents(IN HANDLE ReadyEvent,IN HANDLE RequestEvent,IN HANDLE DoneEvent)190 DestroySoundThreadEvents(
191 IN HANDLE ReadyEvent,
192 IN HANDLE RequestEvent,
193 IN HANDLE DoneEvent)
194 {
195 VALIDATE_MMSYS_PARAMETER( ReadyEvent != INVALID_HANDLE_VALUE );
196 VALIDATE_MMSYS_PARAMETER( RequestEvent != INVALID_HANDLE_VALUE );
197 VALIDATE_MMSYS_PARAMETER( DoneEvent != INVALID_HANDLE_VALUE );
198
199 SND_TRACE(L"Destroying thread events\n");
200
201 CloseHandle(ReadyEvent);
202 CloseHandle(RequestEvent);
203 CloseHandle(DoneEvent);
204
205 return MMSYSERR_NOERROR;
206 }
207
208 MMRESULT
CreateSoundThread(OUT PSOUND_THREAD * Thread)209 CreateSoundThread(
210 OUT PSOUND_THREAD* Thread)
211 {
212 MMRESULT Result;
213 PSOUND_THREAD NewThread;
214
215 VALIDATE_MMSYS_PARAMETER( Thread );
216
217 NewThread = AllocateStruct(SOUND_THREAD);
218 if ( ! NewThread )
219 return MMSYSERR_NOMEM;
220
221 /* Prepare the events we'll be using to sync. everything */
222 Result = CreateSoundThreadEvents(&NewThread->Events.Ready,
223 &NewThread->Events.Request,
224 &NewThread->Events.Done);
225
226 if ( ! MMSUCCESS(Result) )
227 {
228 FreeMemory(NewThread);
229 return TranslateInternalMmResult(Result);
230 }
231
232 SND_TRACE(L"Creating a sound thread\n");
233 NewThread->Handle = CreateThread(NULL,
234 0,
235 &SoundThreadMain,
236 (LPVOID) NewThread,
237 CREATE_SUSPENDED,
238 NULL);
239
240 /* Something went wrong, bail out! */
241 if ( NewThread->Handle == INVALID_HANDLE_VALUE )
242 {
243 SND_ERR(L"Sound thread creation failed!\n");
244 DestroySoundThreadEvents(NewThread->Events.Ready,
245 NewThread->Events.Request,
246 NewThread->Events.Done);
247
248 FreeMemory(NewThread);
249
250 return Win32ErrorToMmResult(GetLastError());
251 }
252
253 /* Wake the thread up */
254 if ( ResumeThread(NewThread->Handle) == -1 )
255 {
256 SND_ERR(L"Failed to resume thread!\n");
257 CloseHandle(NewThread->Handle);
258 DestroySoundThreadEvents(NewThread->Events.Ready,
259 NewThread->Events.Request,
260 NewThread->Events.Done);
261
262 FreeMemory(NewThread);
263 return Win32ErrorToMmResult(GetLastError());
264 }
265
266 /* If all is well we can now give the thread to the caller */
267 *Thread = NewThread;
268 return MMSYSERR_NOERROR;
269 }
270
271 MMRESULT
DestroySoundThread(IN PSOUND_THREAD Thread)272 DestroySoundThread(
273 IN PSOUND_THREAD Thread)
274 {
275 VALIDATE_MMSYS_PARAMETER( Thread );
276 SND_ASSERT( Thread->Handle != INVALID_HANDLE_VALUE );
277
278 SND_TRACE(L"Terminating sound thread\n");
279
280 /* Tell the thread to terminate itself */
281 TerminateSoundThread(Thread);
282
283 SND_TRACE(L"Sound thread terminated, performing cleanup of thread resources\n");
284
285 CloseHandle(Thread->Handle); /* Is this needed? */
286 Thread->Handle = INVALID_HANDLE_VALUE;
287
288 DestroySoundThreadEvents(Thread->Events.Ready,
289 Thread->Events.Request,
290 Thread->Events.Done);
291
292 /* Wipe and free the memory used for the thread */
293 ZeroMemory(Thread, sizeof(SOUND_THREAD));
294 FreeMemory(Thread);
295
296 SND_TRACE(L"Finished thread cleanup\n");
297
298 return MMSYSERR_NOERROR;
299 }
300
301