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"
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
29 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry
;
30 struct SystemClockAdviseEntry
{
31 SystemClockAdviseEntry
* next
;
32 SystemClockAdviseEntry
* prev
;
35 REFERENCE_TIME rtBaseTime
;
36 REFERENCE_TIME rtIntervalTime
;
39 typedef struct SystemClockImpl
{
40 IReferenceClock IReferenceClock_iface
;
43 /** IReferenceClock */
46 BOOL adviseThreadActive
;
47 REFERENCE_TIME lastRefTime
;
48 DWORD lastTimeTickCount
;
49 CRITICAL_SECTION safe
;
51 SystemClockAdviseEntry
* pSingleShotAdvise
;
52 SystemClockAdviseEntry
* pPeriodicAdvise
;
55 static inline SystemClockImpl
*impl_from_IReferenceClock(IReferenceClock
*iface
)
57 return CONTAINING_RECORD(iface
, SystemClockImpl
, IReferenceClock_iface
);
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
;
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
;
73 for (it
= *pQueue
; NULL
!= it
&& (it
->rtBaseTime
+ it
->rtIntervalTime
) < bornTime
; it
= it
->next
) {
76 if (NULL
== prev_it
) {
78 if (NULL
!= (*pQueue
)) pEntry
->next
= (*pQueue
)->next
;
79 /*assert( NULL == pEntry->next->prev );*/
80 if (NULL
!= pEntry
->next
) pEntry
->next
->prev
= pEntry
;
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
;
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)
96 static DWORD WINAPI
SystemClockAdviseThread(LPVOID lpParam
) {
97 SystemClockImpl
* This
= lpParam
;
98 DWORD timeOut
= INFINITE
;
102 REFERENCE_TIME curTime
;
103 SystemClockAdviseEntry
* it
= NULL
;
105 TRACE("(%p): Main Loop\n", This
);
108 if (timeOut
> 0) MsgWaitForMultipleObjects(0, NULL
, FALSE
, timeOut
, QS_POSTMESSAGE
|QS_SENDMESSAGE
|QS_TIMER
);
110 EnterCriticalSection(&This
->safe
);
111 /*timeOut = IReferenceClock_OnTimerUpdated(This); */
112 hr
= IReferenceClock_GetTime(&This
->IReferenceClock_iface
, &curTime
);
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
);
129 if (NULL
!= it
) timeOut
= (DWORD
) ((it
->rtBaseTime
+ it
->rtIntervalTime
) - curTime
) / (REFERENCE_TIME
)10000;
130 else timeOut
= INFINITE
;
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 );*/
142 tmpTimeOut
= (DWORD
) ((it
->rtBaseTime
+ it
->rtIntervalTime
) - curTime
) / (REFERENCE_TIME
)10000;
143 if (timeOut
> tmpTimeOut
) timeOut
= tmpTimeOut
;
147 LeaveCriticalSection(&This
->safe
);
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
);
155 switch (msg
.message
) {
159 case ADVISE_ADD_SINGLESHOT
:
160 case ADVISE_ADD_PERIODIC
:
161 /** set timeout to 0 to do a rescan now */
165 /** hmmmm what we can do here ... */
169 ERR("Unhandled message %u. Critical Path\n", msg
.message
);
177 TRACE("(%p): Exiting\n", This
);
180 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
182 static BOOL
SystemClockPostMessageToAdviseThread(SystemClockImpl
* This
, UINT iMsg
) {
183 if (FALSE
== This
->adviseThreadActive
) {
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
;
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
))
199 return PostThreadMessageW(This
->adviseThreadId
, iMsg
, 0, 0);
202 static ULONG WINAPI
SystemClockImpl_AddRef(IReferenceClock
* iface
) {
203 SystemClockImpl
*This
= impl_from_IReferenceClock(iface
);
204 ULONG ref
= InterlockedIncrement(&This
->ref
);
206 TRACE("(%p): AddRef from %d\n", This
, ref
- 1);
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
);
215 if (IsEqualIID (riid
, &IID_IUnknown
) ||
216 IsEqualIID (riid
, &IID_IReferenceClock
)) {
217 SystemClockImpl_AddRef(iface
);
223 WARN("(%p, %s,%p): not found\n", This
, debugstr_guid(riid
), ppobj
);
224 return E_NOINTERFACE
;
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
);
232 if (SystemClockPostMessageToAdviseThread(This
, ADVISE_EXIT
)) {
233 WaitForSingleObject(This
->adviseThread
, INFINITE
);
234 CloseHandle(This
->adviseThread
);
236 This
->safe
.DebugInfo
->Spare
[0] = 0;
237 DeleteCriticalSection(&This
->safe
);
243 static HRESULT WINAPI
SystemClockImpl_GetTime(IReferenceClock
* iface
, REFERENCE_TIME
* pTime
) {
244 SystemClockImpl
*This
= impl_from_IReferenceClock(iface
);
245 DWORD curTimeTickCount
;
248 TRACE("(%p, %p)\n", This
, pTime
);
254 curTimeTickCount
= GetTickCount();
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
);
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
;
269 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This
, wine_dbgstr_longlong(rtBaseTime
),
270 wine_dbgstr_longlong(rtStreamTime
), hEvent
, pdwAdviseCookie
);
275 if (0 >= rtBaseTime
+ rtStreamTime
) {
278 if (NULL
== pdwAdviseCookie
) {
281 pEntry
= CoTaskMemAlloc(sizeof(SystemClockAdviseEntry
));
282 if (NULL
== pEntry
) {
283 return E_OUTOFMEMORY
;
285 ZeroMemory(pEntry
, sizeof(SystemClockAdviseEntry
));
287 pEntry
->hEvent
= (HANDLE
) hEvent
;
288 pEntry
->rtBaseTime
= rtBaseTime
+ rtStreamTime
;
289 pEntry
->rtIntervalTime
= 0;
291 EnterCriticalSection(&This
->safe
);
292 QUARTZ_InsertAviseEntryFromQueue(This
, pEntry
, &This
->pSingleShotAdvise
);
293 LeaveCriticalSection(&This
->safe
);
295 SystemClockPostMessageToAdviseThread(This
, ADVISE_ADD_SINGLESHOT
);
297 *pdwAdviseCookie
= (DWORD_PTR
) (pEntry
);
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
;
305 TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This
, wine_dbgstr_longlong(rtStartTime
),
306 wine_dbgstr_longlong(rtPeriodTime
), hSemaphore
, pdwAdviseCookie
);
311 if (0 >= rtStartTime
|| 0 >= rtPeriodTime
) {
314 if (NULL
== pdwAdviseCookie
) {
317 pEntry
= CoTaskMemAlloc(sizeof(SystemClockAdviseEntry
));
318 if (NULL
== pEntry
) {
319 return E_OUTOFMEMORY
;
321 ZeroMemory(pEntry
, sizeof(SystemClockAdviseEntry
));
323 pEntry
->hEvent
= (HANDLE
) hSemaphore
;
324 pEntry
->rtBaseTime
= rtStartTime
;
325 pEntry
->rtIntervalTime
= rtPeriodTime
;
327 EnterCriticalSection(&This
->safe
);
328 QUARTZ_InsertAviseEntryFromQueue(This
, pEntry
, &This
->pPeriodicAdvise
);
329 LeaveCriticalSection(&This
->safe
);
331 SystemClockPostMessageToAdviseThread(This
, ADVISE_ADD_PERIODIC
);
333 *pdwAdviseCookie
= (DWORD_PTR
) (pEntry
);
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
;
342 TRACE("(%p, %lu)\n", This
, dwAdviseCookie
);
344 pEntry
= (SystemClockAdviseEntry
*) dwAdviseCookie
;
346 EnterCriticalSection(&This
->safe
);
347 for (it
= This
->pPeriodicAdvise
; NULL
!= it
&& it
!= pEntry
; it
= it
->next
) ;
349 for (it
= This
->pSingleShotAdvise
; NULL
!= it
&& it
!= pEntry
; it
= it
->next
) ;
356 QUARTZ_RemoveAviseEntryFromQueue(This
, pEntry
);
357 CoTaskMemFree(pEntry
);
359 SystemClockPostMessageToAdviseThread(This
, ADVISE_REMOVE
);
362 LeaveCriticalSection(&This
->safe
);
366 static const IReferenceClockVtbl SystemClock_Vtbl
=
368 SystemClockImpl_QueryInterface
,
369 SystemClockImpl_AddRef
,
370 SystemClockImpl_Release
,
371 SystemClockImpl_GetTime
,
372 SystemClockImpl_AdviseTime
,
373 SystemClockImpl_AdvisePeriodic
,
374 SystemClockImpl_Unadvise
377 HRESULT
QUARTZ_CreateSystemClock(IUnknown
* pUnkOuter
, LPVOID
* ppv
) {
378 SystemClockImpl
* obj
= NULL
;
380 TRACE("(%p,%p)\n", ppv
, pUnkOuter
);
382 obj
= CoTaskMemAlloc(sizeof(SystemClockImpl
));
385 return E_OUTOFMEMORY
;
387 ZeroMemory(obj
, sizeof(SystemClockImpl
));
389 obj
->IReferenceClock_iface
.lpVtbl
= &SystemClock_Vtbl
;
390 obj
->ref
= 0; /* will be inited by QueryInterface */
392 obj
->lastTimeTickCount
= GetTickCount();
393 InitializeCriticalSection(&obj
->safe
);
394 obj
->safe
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": SystemClockImpl.safe");
396 return SystemClockImpl_QueryInterface(&obj
->IReferenceClock_iface
, &IID_IReferenceClock
, ppv
);