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