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