xref: /reactos/sdk/lib/drivers/sound/mmebuddy/thread.c (revision 84ccccab)
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
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
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
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
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
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
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
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
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