Synchronize with trunk.
[reactos.git] / dll / directx / 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 <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
131 /** Now Periodics Advice: semi sorted list (sort cannot be used) */
132 for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
133 if (it->rtBaseTime <= curTime) {
134 DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
135 /** Release the semaphore ... */
136 ReleaseSemaphore(it->hEvent, nPeriods, NULL);
137 /** ... and refresh time */
138 it->rtBaseTime += nPeriods * it->rtIntervalTime;
139 /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
140 }
141 tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
142 if (timeOut > tmpTimeOut) timeOut = tmpTimeOut;
143 }
144
145 outrefresh:
146 LeaveCriticalSection(&This->safe);
147
148 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
149 /** if hwnd we suppose that is a windows event ... */
150 if (NULL != msg.hwnd) {
151 TranslateMessage(&msg);
152 DispatchMessageW(&msg);
153 } else {
154 switch (msg.message) {
155 case WM_QUIT:
156 case ADVISE_EXIT:
157 goto outofthread;
158 case ADVISE_ADD_SINGLESHOT:
159 case ADVISE_ADD_PERIODIC:
160 /** set timeout to 0 to do a rescan now */
161 timeOut = 0;
162 break;
163 case ADVISE_REMOVE:
164 /** hmmmm what we can do here ... */
165 timeOut = INFINITE;
166 break;
167 default:
168 ERR("Unhandled message %u. Critical Path\n", msg.message);
169 break;
170 }
171 }
172 }
173 }
174
175 outofthread:
176 TRACE("(%p): Exiting\n", This);
177 return 0;
178 }
179 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
180
181 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
182 if (FALSE == This->adviseThreadActive) {
183 BOOL res;
184 This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
185 if (NULL == This->adviseThread) return FALSE;
186 SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
187 This->adviseThreadActive = TRUE;
188 while(1) {
189 res = PostThreadMessageW(This->adviseThreadId, iMsg, 0, 0);
190 /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */
191 if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID))
192 Sleep(0);
193 else
194 break;
195 }
196 return res;
197 }
198 return PostThreadMessageW(This->adviseThreadId, iMsg, 0, 0);
199 }
200
201 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
202 SystemClockImpl *This = impl_from_IReferenceClock(iface);
203 ULONG ref = InterlockedIncrement(&This->ref);
204
205 TRACE("(%p): AddRef from %d\n", This, ref - 1);
206
207 return ref;
208 }
209
210 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
211 SystemClockImpl *This = impl_from_IReferenceClock(iface);
212 TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
213
214 if (IsEqualIID (riid, &IID_IUnknown) ||
215 IsEqualIID (riid, &IID_IReferenceClock)) {
216 SystemClockImpl_AddRef(iface);
217 *ppobj = This;
218 return S_OK;
219 }
220
221 *ppobj = NULL;
222 WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
223 return E_NOINTERFACE;
224 }
225
226 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
227 SystemClockImpl *This = impl_from_IReferenceClock(iface);
228 ULONG ref = InterlockedDecrement(&This->ref);
229 TRACE("(%p): ReleaseRef to %d\n", This, ref);
230 if (ref == 0) {
231 if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
232 WaitForSingleObject(This->adviseThread, INFINITE);
233 CloseHandle(This->adviseThread);
234 }
235 This->safe.DebugInfo->Spare[0] = 0;
236 DeleteCriticalSection(&This->safe);
237 CoTaskMemFree(This);
238 }
239 return ref;
240 }
241
242 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
243 SystemClockImpl *This = impl_from_IReferenceClock(iface);
244 DWORD curTimeTickCount;
245 HRESULT hr = S_OK;
246
247 TRACE("(%p, %p)\n", This, pTime);
248
249 if (NULL == pTime) {
250 return E_POINTER;
251 }
252
253 curTimeTickCount = GetTickCount();
254
255 EnterCriticalSection(&This->safe);
256 if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
257 This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
258 This->lastTimeTickCount = curTimeTickCount;
259 *pTime = This->lastRefTime;
260 LeaveCriticalSection(&This->safe);
261 return hr;
262 }
263
264 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
265 SystemClockImpl *This = impl_from_IReferenceClock(iface);
266 SystemClockAdviseEntry* pEntry = NULL;
267
268 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtBaseTime),
269 wine_dbgstr_longlong(rtStreamTime), hEvent, pdwAdviseCookie);
270
271 if (!hEvent) {
272 return E_INVALIDARG;
273 }
274 if (0 >= rtBaseTime + rtStreamTime) {
275 return E_INVALIDARG;
276 }
277 if (NULL == pdwAdviseCookie) {
278 return E_POINTER;
279 }
280 pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
281 if (NULL == pEntry) {
282 return E_OUTOFMEMORY;
283 }
284 ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
285
286 pEntry->hEvent = (HANDLE) hEvent;
287 pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
288 pEntry->rtIntervalTime = 0;
289
290 EnterCriticalSection(&This->safe);
291 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
292 LeaveCriticalSection(&This->safe);
293
294 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
295
296 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
297 return S_OK;
298 }
299
300 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
301 SystemClockImpl *This = impl_from_IReferenceClock(iface);
302 SystemClockAdviseEntry* pEntry = NULL;
303
304 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtStartTime),
305 wine_dbgstr_longlong(rtPeriodTime), hSemaphore, pdwAdviseCookie);
306
307 if (!hSemaphore) {
308 return E_INVALIDARG;
309 }
310 if (0 >= rtStartTime || 0 >= rtPeriodTime) {
311 return E_INVALIDARG;
312 }
313 if (NULL == pdwAdviseCookie) {
314 return E_POINTER;
315 }
316 pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
317 if (NULL == pEntry) {
318 return E_OUTOFMEMORY;
319 }
320 ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
321
322 pEntry->hEvent = (HANDLE) hSemaphore;
323 pEntry->rtBaseTime = rtStartTime;
324 pEntry->rtIntervalTime = rtPeriodTime;
325
326 EnterCriticalSection(&This->safe);
327 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
328 LeaveCriticalSection(&This->safe);
329
330 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
331
332 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
333 return S_OK;
334 }
335
336 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
337 SystemClockImpl *This = impl_from_IReferenceClock(iface);
338 SystemClockAdviseEntry* pEntry = NULL;
339 SystemClockAdviseEntry* it = NULL;
340 HRESULT ret = S_OK;
341 TRACE("(%p, %lu)\n", This, dwAdviseCookie);
342
343 pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
344
345 EnterCriticalSection(&This->safe);
346 for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
347 if (it != pEntry) {
348 for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
349 if (it != pEntry) {
350 ret = S_FALSE;
351 goto out;
352 }
353 }
354
355 QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
356 CoTaskMemFree(pEntry);
357
358 SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
359
360 out:
361 LeaveCriticalSection(&This->safe);
362 return ret;
363 }
364
365 static const IReferenceClockVtbl SystemClock_Vtbl =
366 {
367 SystemClockImpl_QueryInterface,
368 SystemClockImpl_AddRef,
369 SystemClockImpl_Release,
370 SystemClockImpl_GetTime,
371 SystemClockImpl_AdviseTime,
372 SystemClockImpl_AdvisePeriodic,
373 SystemClockImpl_Unadvise
374 };
375
376 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
377 SystemClockImpl* obj = NULL;
378
379 TRACE("(%p,%p)\n", ppv, pUnkOuter);
380
381 obj = CoTaskMemAlloc(sizeof(SystemClockImpl));
382 if (NULL == obj) {
383 *ppv = NULL;
384 return E_OUTOFMEMORY;
385 }
386 ZeroMemory(obj, sizeof(SystemClockImpl));
387
388 obj->IReferenceClock_iface.lpVtbl = &SystemClock_Vtbl;
389 obj->ref = 0; /* will be inited by QueryInterface */
390
391 obj->lastTimeTickCount = GetTickCount();
392 InitializeCriticalSection(&obj->safe);
393 obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.safe");
394
395 return SystemClockImpl_QueryInterface(&obj->IReferenceClock_iface, &IID_IReferenceClock, ppv);
396 }