quartz: Check allocation failure and clear memory in DSound Renderer.
[wine] / dlls / quartz / systemclock.c
1 /*
2  * Implementation of IReferenceClock
3  *
4  * Copyright 2004 Raphael Junqueira
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "quartz_private.h"
22
23 #include "wine/debug.h"
24 #include "wine/unicode.h"
25 #include "uuids.h"
26 #include <assert.h>
27
28 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
29
30 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
31 struct SystemClockAdviseEntry {
32   SystemClockAdviseEntry* next;
33   SystemClockAdviseEntry* prev;
34
35   HANDLE           hEvent;
36   REFERENCE_TIME   rtBaseTime;
37   REFERENCE_TIME   rtIntervalTime;
38 };
39
40 typedef struct SystemClockImpl {
41   const IReferenceClockVtbl *lpVtbl;
42   LONG ref;
43
44   /** IReferenceClock */
45   HANDLE         adviseThread;
46   DWORD          adviseThreadId;
47   BOOL           adviseThreadActive;
48   REFERENCE_TIME lastRefTime;
49   DWORD          lastTimeTickCount;
50   CRITICAL_SECTION safe;
51
52   SystemClockAdviseEntry* pSingleShotAdvise;
53   SystemClockAdviseEntry* pPeriodicAdvise;
54 } SystemClockImpl;
55
56
57 static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
58   if (pEntry->prev) pEntry->prev->next = pEntry->next;
59   if (pEntry->next) pEntry->next->prev = pEntry->prev;
60   if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
61   if (This->pPeriodicAdvise == pEntry)    This->pPeriodicAdvise = pEntry->next;
62 }
63
64 static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
65   SystemClockAdviseEntry* prev_it = NULL;
66   SystemClockAdviseEntry* it = NULL;
67   REFERENCE_TIME bornTime =  pEntry->rtBaseTime + pEntry->rtIntervalTime;
68
69   for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
70     prev_it = it;
71   }
72   if (NULL == prev_it) {
73     pEntry->prev = NULL;
74     if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
75     /*assert( NULL == pEntry->next->prev );*/
76     if (NULL != pEntry->next) pEntry->next->prev = pEntry;
77     (*pQueue) = pEntry;
78   } else {
79     pEntry->prev = prev_it;
80     pEntry->next = prev_it->next;
81     prev_it->next = pEntry;
82     if (NULL != pEntry->next) pEntry->next->prev = pEntry;
83   }
84 }
85
86 #define MAX_REFTIME            (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
87 #define ADVISE_EXIT            (WM_APP + 0)
88 #define ADVISE_REMOVE          (WM_APP + 2)
89 #define ADVISE_ADD_SINGLESHOT  (WM_APP + 4)
90 #define ADVISE_ADD_PERIODIC    (WM_APP + 8)
91
92 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
93   SystemClockImpl* This = (SystemClockImpl*) lpParam;
94   DWORD timeOut = INFINITE;
95   DWORD tmpTimeOut;
96   MSG msg;
97   HRESULT hr;
98   REFERENCE_TIME curTime;
99   SystemClockAdviseEntry* it = NULL;
100
101   TRACE("(%p): Main Loop\n", This);
102
103   while (TRUE) {
104     if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
105     
106     EnterCriticalSection(&This->safe);
107     /*timeOut = IReferenceClock_OnTimerUpdated(This); */
108     hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
109     if (FAILED(hr)) {
110       timeOut = INFINITE;
111       goto outrefresh;
112     }
113
114     /** First SingleShots Advice: sorted list */
115     for (it = This->pSingleShotAdvise; NULL != it && (it->rtBaseTime + it->rtIntervalTime) <= curTime; it = it->next) {
116       /** send event ... */
117       SetEvent((HANDLE) it->hEvent);
118       /** ... and Release it */
119       QUARTZ_RemoveAviseEntryFromQueue(This, it);
120       CoTaskMemFree(it);
121     }
122     if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
123
124     /** Now Periodics Advice: semi sorted list (sort cannot be used) */
125     for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
126       if (it->rtBaseTime <= curTime) {
127         DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
128         /** Release the semaphore ... */
129         ReleaseSemaphore((HANDLE) it->hEvent, nPeriods, NULL);
130         /** ... and refresh time */
131         it->rtBaseTime += nPeriods * it->rtIntervalTime;
132         /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
133       }
134       tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
135       if (timeOut > tmpTimeOut) timeOut = tmpTimeOut; 
136     }
137
138 outrefresh:
139     LeaveCriticalSection(&This->safe);
140     
141     while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
142       /** if hwnd we suppose that is a windows event ... */
143       if  (NULL != msg.hwnd) {
144         TranslateMessage(&msg);
145         DispatchMessageA(&msg);
146       } else {
147         switch (msg.message) {      
148         case WM_QUIT:
149         case ADVISE_EXIT:
150           goto outofthread;
151         case ADVISE_ADD_SINGLESHOT:
152         case ADVISE_ADD_PERIODIC:
153           /** set timeout to 0 to do a rescan now */
154           timeOut = 0;
155           break;
156         case ADVISE_REMOVE:
157           /** hmmmm what we can do here ... */
158           timeOut = INFINITE;
159           break;
160         default:
161           ERR("Unhandled message %u. Critical Path\n", msg.message);
162           break;
163         }
164       }
165     }
166   }
167
168 outofthread:
169   TRACE("(%p): Exiting\n", This);
170   return 0;
171 }
172 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
173
174 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
175   if (FALSE == This->adviseThreadActive) {
176     BOOL res;
177     This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
178     if (NULL == This->adviseThread) return FALSE;
179     SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
180     This->adviseThreadActive = TRUE;
181     while(1) {
182       res = PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
183       /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */
184       if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID))
185         Sleep(0);
186       else
187         break;
188     }
189     return res;
190   }
191   return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
192 }
193
194 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
195   SystemClockImpl *This = (SystemClockImpl *)iface;
196   ULONG ref = InterlockedIncrement(&This->ref);
197
198   TRACE("(%p): AddRef from %d\n", This, ref - 1);
199
200   return ref;
201 }
202
203 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
204   SystemClockImpl *This = (SystemClockImpl *)iface;
205   TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
206   
207   if (IsEqualIID (riid, &IID_IUnknown) || 
208       IsEqualIID (riid, &IID_IReferenceClock)) {
209     SystemClockImpl_AddRef(iface);
210     *ppobj = This;
211     return S_OK;
212   }
213   
214   WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
215   return E_NOINTERFACE;
216 }
217
218 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
219   SystemClockImpl *This = (SystemClockImpl *)iface;
220   ULONG ref = InterlockedDecrement(&This->ref);
221   TRACE("(%p): ReleaseRef to %d\n", This, ref);
222   if (ref == 0) {
223     if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
224       WaitForSingleObject(This->adviseThread, INFINITE);
225       CloseHandle(This->adviseThread);
226     }
227     DeleteCriticalSection(&This->safe);
228     CoTaskMemFree(This);
229   }
230   return ref;
231 }
232
233 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
234   SystemClockImpl *This = (SystemClockImpl *)iface;
235   DWORD curTimeTickCount;
236   HRESULT hr = S_OK;
237
238   TRACE("(%p, %p)\n", This, pTime);
239
240   if (NULL == pTime) {
241     return E_POINTER;
242   }
243
244   curTimeTickCount = GetTickCount();
245
246   EnterCriticalSection(&This->safe);
247   /** TODO: safe this not using * 10000 */
248   This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
249   This->lastTimeTickCount = curTimeTickCount;
250   LeaveCriticalSection(&This->safe);
251
252   *pTime = This->lastRefTime;
253   if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
254   This->lastTimeTickCount = curTimeTickCount;
255   return hr;
256 }
257
258 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
259   SystemClockImpl *This = (SystemClockImpl *)iface;
260   SystemClockAdviseEntry* pEntry = NULL;
261
262   TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtBaseTime),
263       wine_dbgstr_longlong(rtStreamTime), hEvent, pdwAdviseCookie);
264
265   if ((HEVENT) 0 == hEvent) {
266     return E_INVALIDARG;
267   }
268   if (0 >= rtBaseTime + rtStreamTime) {
269     return E_INVALIDARG;
270   }
271   if (NULL == pdwAdviseCookie) {
272     return E_POINTER;
273   }
274   pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
275   if (NULL == pEntry) {
276     return E_OUTOFMEMORY;
277   }
278   ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
279
280   pEntry->hEvent = (HANDLE) hEvent;
281   pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
282   pEntry->rtIntervalTime = 0;
283
284   EnterCriticalSection(&This->safe);
285   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
286   LeaveCriticalSection(&This->safe);
287
288   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
289
290   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
291   return S_OK;
292 }
293
294 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
295   SystemClockImpl *This = (SystemClockImpl *)iface;
296   SystemClockAdviseEntry* pEntry = NULL;
297
298   TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtStartTime),
299       wine_dbgstr_longlong(rtPeriodTime), hSemaphore, pdwAdviseCookie);
300
301   if ((HSEMAPHORE) 0 == hSemaphore) {
302     return E_INVALIDARG;
303   }
304   if (0 >= rtStartTime || 0 >= rtPeriodTime) {
305     return E_INVALIDARG;
306   }
307   if (NULL == pdwAdviseCookie) {
308     return E_POINTER;
309   }
310   pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
311   if (NULL == pEntry) {
312     return E_OUTOFMEMORY;
313   }
314   ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
315
316   pEntry->hEvent = (HANDLE) hSemaphore;
317   pEntry->rtBaseTime = rtStartTime;
318   pEntry->rtIntervalTime = rtPeriodTime;
319
320   EnterCriticalSection(&This->safe);
321   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
322   LeaveCriticalSection(&This->safe);
323
324   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
325
326   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
327   return S_OK;
328 }
329
330 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
331   SystemClockImpl *This = (SystemClockImpl *)iface;
332   SystemClockAdviseEntry* pEntry = NULL;
333   SystemClockAdviseEntry* it = NULL;
334   HRESULT ret = S_OK;
335   TRACE("(%p, %lu)\n", This, dwAdviseCookie);
336
337   pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
338
339   EnterCriticalSection(&This->safe);
340   for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
341   if (it != pEntry) {
342     for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
343     if (it != pEntry) {
344       ret = S_FALSE;
345       goto out;
346     }
347   }
348
349   QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
350   CoTaskMemFree(pEntry);
351
352   SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
353
354 out:
355   LeaveCriticalSection(&This->safe);
356   return ret;
357 }
358
359 static const IReferenceClockVtbl SystemClock_Vtbl = 
360 {
361     SystemClockImpl_QueryInterface,
362     SystemClockImpl_AddRef,
363     SystemClockImpl_Release,
364     SystemClockImpl_GetTime,
365     SystemClockImpl_AdviseTime,
366     SystemClockImpl_AdvisePeriodic,
367     SystemClockImpl_Unadvise
368 };
369
370 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
371   SystemClockImpl* obj = NULL;
372   
373   TRACE("(%p,%p)\n", ppv, pUnkOuter);
374   
375   obj = CoTaskMemAlloc(sizeof(SystemClockImpl));
376   if (NULL == obj)      {
377     *ppv = NULL;
378     return E_OUTOFMEMORY;
379   }
380   ZeroMemory(obj, sizeof(SystemClockImpl));
381
382   obj->lpVtbl = &SystemClock_Vtbl;
383   obj->ref = 0;  /* will be inited by QueryInterface */
384
385   obj->lastTimeTickCount = GetTickCount();
386   InitializeCriticalSection(&obj->safe);
387
388   return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);
389 }