2 * Implementation of IReferenceClock
4 * Copyright 2004 Raphael Junqueira
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.
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.
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
21 #include "quartz_private.h"
23 #include "wine/debug.h"
24 #include "wine/unicode.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
30 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
31 struct SystemClockAdviseEntry {
32 SystemClockAdviseEntry* next;
33 SystemClockAdviseEntry* prev;
36 REFERENCE_TIME rtBaseTime;
37 REFERENCE_TIME rtIntervalTime;
40 typedef struct SystemClockImpl {
41 const IReferenceClockVtbl *lpVtbl;
44 /** IReferenceClock */
47 BOOL adviseThreadActive;
48 REFERENCE_TIME lastRefTime;
49 DWORD lastTimeTickCount;
50 CRITICAL_SECTION safe;
52 SystemClockAdviseEntry* pSingleShotAdvise;
53 SystemClockAdviseEntry* pPeriodicAdvise;
57 static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
58 if (pEntry->prev) pEntry->prev->next = pEntry->next;
59 if (pEntry->next) pEntry->next->prev = pEntry->prev;
60 if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
61 if (This->pPeriodicAdvise == pEntry) This->pPeriodicAdvise = pEntry->next;
64 static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
65 SystemClockAdviseEntry* prev_it = NULL;
66 SystemClockAdviseEntry* it = NULL;
67 REFERENCE_TIME bornTime = pEntry->rtBaseTime + pEntry->rtIntervalTime;
69 for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
72 if (NULL == prev_it) {
74 if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
75 /*assert( NULL == pEntry->next->prev );*/
76 if (NULL != pEntry->next) pEntry->next->prev = pEntry;
79 pEntry->prev = prev_it;
80 pEntry->next = prev_it->next;
81 prev_it->next = pEntry;
82 if (NULL != pEntry->next) pEntry->next->prev = pEntry;
86 #define MAX_REFTIME (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
87 #define ADVISE_EXIT (WM_APP + 0)
88 #define ADVISE_REMOVE (WM_APP + 2)
89 #define ADVISE_ADD_SINGLESHOT (WM_APP + 4)
90 #define ADVISE_ADD_PERIODIC (WM_APP + 8)
92 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
93 SystemClockImpl* This = (SystemClockImpl*) lpParam;
94 DWORD timeOut = INFINITE;
98 REFERENCE_TIME curTime;
99 SystemClockAdviseEntry* it = NULL;
101 TRACE("(%p): Main Loop\n", This);
104 if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
106 EnterCriticalSection(&This->safe);
107 /*timeOut = IReferenceClock_OnTimerUpdated(This); */
108 hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
114 /** First SingleShots Advice: sorted list */
115 for (it = This->pSingleShotAdvise; NULL != it && (it->rtBaseTime + it->rtIntervalTime) <= curTime; it = it->next) {
116 /** send event ... */
117 SetEvent((HANDLE) it->hEvent);
118 /** ... and Release it */
119 QUARTZ_RemoveAviseEntryFromQueue(This, it);
120 HeapFree(GetProcessHeap(), 0, it);
122 if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
124 /** Now Periodics Advice: semi sorted list (sort cannot be used) */
125 for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
126 if (it->rtBaseTime <= curTime) {
127 DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
128 /** Release the semaphore ... */
129 ReleaseSemaphore((HANDLE) it->hEvent, nPeriods, NULL);
130 /** ... and refresh time */
131 it->rtBaseTime += nPeriods * it->rtIntervalTime;
132 /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
134 tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
135 if (timeOut > tmpTimeOut) timeOut = tmpTimeOut;
139 LeaveCriticalSection(&This->safe);
141 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
142 /** if hwnd we suppose that is a windows event ... */
143 if (NULL != msg.hwnd) {
144 TranslateMessage(&msg);
145 DispatchMessageA(&msg);
147 switch (msg.message) {
151 case ADVISE_ADD_SINGLESHOT:
152 case ADVISE_ADD_PERIODIC:
153 /** set timeout to 0 to do a rescan now */
157 /** hmmmm what we can do here ... */
161 ERR("Unhandled message %u. Critical Path\n", msg.message);
169 TRACE("(%p): Exiting\n", This);
172 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
174 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
175 if (FALSE == This->adviseThreadActive) {
177 This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
178 if (NULL == This->adviseThread) return FALSE;
179 SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
180 This->adviseThreadActive = TRUE;
182 res = PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
183 /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */
184 if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID))
191 return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
194 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
195 SystemClockImpl *This = (SystemClockImpl *)iface;
196 ULONG ref = InterlockedIncrement(&This->ref);
198 TRACE("(%p): AddRef from %d\n", This, ref - 1);
203 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
204 SystemClockImpl *This = (SystemClockImpl *)iface;
205 TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
207 if (IsEqualIID (riid, &IID_IUnknown) ||
208 IsEqualIID (riid, &IID_IReferenceClock)) {
209 SystemClockImpl_AddRef(iface);
214 WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
215 return E_NOINTERFACE;
218 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
219 SystemClockImpl *This = (SystemClockImpl *)iface;
220 ULONG ref = InterlockedDecrement(&This->ref);
221 TRACE("(%p): ReleaseRef to %d\n", This, ref);
223 if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
224 WaitForSingleObject(This->adviseThread, INFINITE);
225 CloseHandle(This->adviseThread);
227 DeleteCriticalSection(&This->safe);
228 HeapFree(GetProcessHeap(), 0, This);
233 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
234 SystemClockImpl *This = (SystemClockImpl *)iface;
235 DWORD curTimeTickCount;
238 TRACE("(%p, %p)\n", This, pTime);
244 curTimeTickCount = GetTickCount();
246 EnterCriticalSection(&This->safe);
247 /** TODO: safe this not using * 10000 */
248 This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
249 This->lastTimeTickCount = curTimeTickCount;
250 LeaveCriticalSection(&This->safe);
252 *pTime = This->lastRefTime;
253 if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
254 This->lastTimeTickCount = curTimeTickCount;
258 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
259 SystemClockImpl *This = (SystemClockImpl *)iface;
260 SystemClockAdviseEntry* pEntry = NULL;
262 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtBaseTime),
263 wine_dbgstr_longlong(rtStreamTime), hEvent, pdwAdviseCookie);
265 if ((HEVENT) 0 == hEvent) {
268 if (0 >= rtBaseTime + rtStreamTime) {
271 if (NULL == pdwAdviseCookie) {
274 pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
275 if (NULL == pEntry) {
276 return E_OUTOFMEMORY;
279 pEntry->hEvent = (HANDLE) hEvent;
280 pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
281 pEntry->rtIntervalTime = 0;
283 EnterCriticalSection(&This->safe);
284 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
285 LeaveCriticalSection(&This->safe);
287 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
289 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
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;
297 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtStartTime),
298 wine_dbgstr_longlong(rtPeriodTime), hSemaphore, pdwAdviseCookie);
300 if ((HSEMAPHORE) 0 == hSemaphore) {
303 if (0 >= rtStartTime || 0 >= rtPeriodTime) {
306 if (NULL == pdwAdviseCookie) {
309 pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
310 if (NULL == pEntry) {
311 return E_OUTOFMEMORY;
314 pEntry->hEvent = (HANDLE) hSemaphore;
315 pEntry->rtBaseTime = rtStartTime;
316 pEntry->rtIntervalTime = rtPeriodTime;
318 EnterCriticalSection(&This->safe);
319 QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
320 LeaveCriticalSection(&This->safe);
322 SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
324 *pdwAdviseCookie = (DWORD_PTR) (pEntry);
328 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
329 SystemClockImpl *This = (SystemClockImpl *)iface;
330 SystemClockAdviseEntry* pEntry = NULL;
331 SystemClockAdviseEntry* it = NULL;
333 TRACE("(%p, %lu)\n", This, dwAdviseCookie);
335 pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
337 EnterCriticalSection(&This->safe);
338 for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
340 for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
347 QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
348 HeapFree(GetProcessHeap(), 0, pEntry);
350 SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
353 LeaveCriticalSection(&This->safe);
357 static const IReferenceClockVtbl SystemClock_Vtbl =
359 SystemClockImpl_QueryInterface,
360 SystemClockImpl_AddRef,
361 SystemClockImpl_Release,
362 SystemClockImpl_GetTime,
363 SystemClockImpl_AdviseTime,
364 SystemClockImpl_AdvisePeriodic,
365 SystemClockImpl_Unadvise
368 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
369 SystemClockImpl* obj = NULL;
371 TRACE("(%p,%p)\n", ppv, pUnkOuter);
373 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockImpl));
376 return E_OUTOFMEMORY;
378 obj->lpVtbl = &SystemClock_Vtbl;
379 obj->ref = 0; /* will be inited by QueryInterface */
381 obj->lastTimeTickCount = GetTickCount();
382 InitializeCriticalSection(&obj->safe);
384 return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);