make_dlls: Recursively ignore testlist.c in all tests directories.
[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 #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   const IReferenceClockVtbl *lpVtbl;
43   LONG 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 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
196   SystemClockImpl *This = (SystemClockImpl *)iface;
197   ULONG ref = InterlockedIncrement(&This->ref);
198
199   TRACE("(%p): AddRef from %ld\n", This, ref - 1);
200
201   return ref;
202 }
203
204 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
205   SystemClockImpl *This = (SystemClockImpl *)iface;
206   TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
207   
208   if (IsEqualIID (riid, &IID_IUnknown) || 
209       IsEqualIID (riid, &IID_IReferenceClock)) {
210     SystemClockImpl_AddRef(iface);
211     *ppobj = This;
212     return S_OK;
213   }
214   
215   WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
216   return E_NOINTERFACE;
217 }
218
219 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
220   SystemClockImpl *This = (SystemClockImpl *)iface;
221   ULONG ref = InterlockedDecrement(&This->ref);
222   TRACE("(%p): ReleaseRef to %ld\n", This, ref);
223   if (ref == 0) {
224     if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
225       WaitForSingleObject(This->adviseThread, INFINITE);
226       CloseHandle(This->adviseThread);
227     }
228     DeleteCriticalSection(&This->safe);
229     HeapFree(GetProcessHeap(), 0, This);
230   }
231   return ref;
232 }
233
234 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
235   SystemClockImpl *This = (SystemClockImpl *)iface;
236   DWORD curTimeTickCount;
237   HRESULT hr = S_OK;
238
239   TRACE("(%p, %p)\n", This, pTime);
240
241   if (NULL == pTime) {
242     return E_POINTER;
243   }
244
245   curTimeTickCount = GetTickCount();
246
247   EnterCriticalSection(&This->safe);
248   /** TODO: safe this not using * 10000 */
249   This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
250   This->lastTimeTickCount = curTimeTickCount;
251   LeaveCriticalSection(&This->safe);
252
253   *pTime = This->lastRefTime;
254   if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
255   This->lastTimeTickCount = curTimeTickCount;
256   return hr;
257 }
258
259 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
260   SystemClockImpl *This = (SystemClockImpl *)iface;
261   SystemClockAdviseEntry* pEntry = NULL;
262
263   TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtBaseTime, 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
275   if (NULL == pEntry) {
276     return E_OUTOFMEMORY;
277   }
278
279   pEntry->hEvent = (HANDLE) hEvent;
280   pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
281   pEntry->rtIntervalTime = 0;
282
283   EnterCriticalSection(&This->safe);
284   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
285   LeaveCriticalSection(&This->safe);
286
287   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
288
289   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
290   return S_OK;
291 }
292
293 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
294   SystemClockImpl *This = (SystemClockImpl *)iface;
295   SystemClockAdviseEntry* pEntry = NULL;
296
297   TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtStartTime, rtPeriodTime, hSemaphore, pdwAdviseCookie);
298
299   if ((HSEMAPHORE) 0 == hSemaphore) {
300     return E_INVALIDARG;
301   }
302   if (0 >= rtStartTime || 0 >= rtPeriodTime) {
303     return E_INVALIDARG;
304   }
305   if (NULL == pdwAdviseCookie) {
306     return E_POINTER;
307   }
308   pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
309   if (NULL == pEntry) {
310     return E_OUTOFMEMORY;
311   }
312
313   pEntry->hEvent = (HANDLE) hSemaphore;
314   pEntry->rtBaseTime = rtStartTime;
315   pEntry->rtIntervalTime = rtPeriodTime;
316
317   EnterCriticalSection(&This->safe);
318   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
319   LeaveCriticalSection(&This->safe);
320
321   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
322
323   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
324   return S_OK;
325 }
326
327 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
328   SystemClockImpl *This = (SystemClockImpl *)iface;
329   SystemClockAdviseEntry* pEntry = NULL;
330   SystemClockAdviseEntry* it = NULL;
331   HRESULT ret = S_OK;
332   TRACE("(%p, %lu)\n", This, dwAdviseCookie);
333
334   pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
335
336   EnterCriticalSection(&This->safe);
337   for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
338   if (it != pEntry) {
339     for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
340     if (it != pEntry) {
341       ret = S_FALSE;
342       goto out;
343     }
344   }
345
346   QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
347   HeapFree(GetProcessHeap(), 0, pEntry);
348
349   SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
350
351 out:
352   LeaveCriticalSection(&This->safe);
353   return ret;
354 }
355
356 static const IReferenceClockVtbl SystemClock_Vtbl = 
357 {
358     SystemClockImpl_QueryInterface,
359     SystemClockImpl_AddRef,
360     SystemClockImpl_Release,
361     SystemClockImpl_GetTime,
362     SystemClockImpl_AdviseTime,
363     SystemClockImpl_AdvisePeriodic,
364     SystemClockImpl_Unadvise
365 };
366
367 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
368   SystemClockImpl* obj = NULL;
369   
370   TRACE("(%p,%p)\n", ppv, pUnkOuter);
371   
372   obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockImpl));
373   if (NULL == obj)      {
374     *ppv = NULL;
375     return E_OUTOFMEMORY;
376   }
377   obj->lpVtbl = &SystemClock_Vtbl;
378   obj->ref = 0;  /* will be inited by QueryInterface */
379
380   obj->lastTimeTickCount = GetTickCount();
381   InitializeCriticalSection(&obj->safe);
382
383   return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);
384 }