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