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