2 * Implementation of CLSID_SystemClock.
4 * hidenori@a2.ctktv.ne.jp
17 #include "debugtools.h"
18 DEFAULT_DEBUG_CHANNEL(quartz
);
20 #include "quartz_private.h"
24 /***************************************************************************
26 * new/delete for CLSID_SystemClock
30 /* can I use offsetof safely? - FIXME? */
31 static QUARTZ_IFEntry IFEntries
[] =
33 { &IID_IReferenceClock
, offsetof(CSystemClock
,refclk
)-offsetof(CSystemClock
,unk
) },
37 static void QUARTZ_DestroySystemClock(IUnknown
* punk
)
39 CSystemClock_THIS(punk
,unk
);
41 CSystemClock_UninitIReferenceClock( This
);
44 HRESULT
QUARTZ_CreateSystemClock(IUnknown
* punkOuter
,void** ppobj
)
49 TRACE("(%p,%p)\n",punkOuter
,ppobj
);
51 psc
= (CSystemClock
*)QUARTZ_AllocObj( sizeof(CSystemClock
) );
55 QUARTZ_IUnkInit( &psc
->unk
, punkOuter
);
56 hr
= CSystemClock_InitIReferenceClock( psc
);
59 QUARTZ_FreeObj( psc
);
63 psc
->unk
.pEntries
= IFEntries
;
64 psc
->unk
.dwEntries
= sizeof(IFEntries
)/sizeof(IFEntries
[0]);
65 psc
->unk
.pOnFinalRelease
= QUARTZ_DestroySystemClock
;
67 *ppobj
= (void*)(&psc
->unk
);
73 /***************************************************************************
75 * CLSID_SystemClock::IReferenceClock
79 #define QUARTZ_MSG_ADDTIMER (WM_APP+0)
80 #define QUARTZ_MSG_REMOVETIMER (WM_APP+1)
81 #define QUARTZ_MSG_EXITTHREAD (WM_APP+2)
84 /****************************************************************************/
86 static QUARTZ_TimerEntry
* IReferenceClock_AllocTimerEntry(CSystemClock
* This
)
88 QUARTZ_TimerEntry
* pEntry
;
91 pEntry
= &This
->m_timerEntries
[0];
92 for ( dw
= 0; dw
< WINE_QUARTZ_SYSCLOCK_TIMER_MAX
; dw
++ )
94 if ( pEntry
->hEvent
== (HANDLE
)NULL
)
102 static QUARTZ_TimerEntry
* IReferenceClock_SearchTimer(CSystemClock
* This
, DWORD dwAdvCookie
)
104 QUARTZ_TimerEntry
* pEntry
;
107 pEntry
= &This
->m_timerEntries
[0];
108 for ( dw
= 0; dw
< WINE_QUARTZ_SYSCLOCK_TIMER_MAX
; dw
++ )
110 if ( pEntry
->hEvent
!= (HANDLE
)NULL
&&
111 pEntry
->dwAdvCookie
== dwAdvCookie
)
119 static DWORD
IReferenceClock_OnTimerUpdated(CSystemClock
* This
)
121 QUARTZ_TimerEntry
* pEntry
;
122 REFERENCE_TIME rtCur
;
123 REFERENCE_TIME rtSignal
;
124 REFERENCE_TIME rtCount
;
128 DWORD dwTimeout
= INFINITE
;
131 hr
= IReferenceClock_GetTime((IReferenceClock
*)(&This
->refclk
),&rtCur
);
135 pEntry
= &This
->m_timerEntries
[0];
136 for ( dw
= 0; dw
< WINE_QUARTZ_SYSCLOCK_TIMER_MAX
; dw
++ )
138 if ( pEntry
->hEvent
!= (HANDLE
)NULL
)
140 rtSignal
= pEntry
->rtStart
+ pEntry
->rtInterval
;
141 if ( rtCur
>= rtSignal
)
143 if ( pEntry
->fPeriodic
)
145 rtCount
= ((rtCur
- pEntry
->rtStart
) / pEntry
->rtInterval
);
146 lCount
= ( rtCount
> (REFERENCE_TIME
)0x7fffffff ) ?
147 (LONG
)0x7fffffff : (LONG
)rtCount
;
148 if ( !ReleaseSemaphore( pEntry
->hEvent
, lCount
, NULL
) )
152 if ( !ReleaseSemaphore( pEntry
->hEvent
, 1, NULL
) )
160 TRACE( "signal an event\n" );
161 SetEvent( pEntry
->hEvent
);
162 pEntry
->hEvent
= (HANDLE
)NULL
;
167 rtCount
= rtSignal
- rtCur
;
168 /* [100ns] -> [ms] */
169 rtCount
= (rtCount
+(REFERENCE_TIME
)9999)/(REFERENCE_TIME
)10000;
170 dwTimeoutCur
= (rtCount
>= 0xfffffffe) ? (DWORD
)0xfffffffe : (DWORD
)rtCount
;
171 if ( dwTimeout
> dwTimeoutCur
)
172 dwTimeout
= dwTimeoutCur
;
182 DWORD WINAPI
IReferenceClock_TimerEntry( LPVOID lpvParam
)
184 CSystemClock
* This
= (CSystemClock
*)lpvParam
;
189 /* initialize the message queue. */
190 PeekMessageA( &msg
, (HWND
)NULL
, 0, 0, PM_NOREMOVE
);
191 /* resume the owner thread. */
192 SetEvent( This
->m_hEventInit
);
194 TRACE( "Enter message loop.\n" );
197 dwTimeout
= INFINITE
;
202 dwRes
= MsgWaitForMultipleObjects(
208 EnterCriticalSection( &This
->m_csClock
);
209 dwTimeout
= IReferenceClock_OnTimerUpdated(This
);
210 LeaveCriticalSection( &This
->m_csClock
);
211 TRACE( "catch an event / timeout %lu\n", dwTimeout
);
213 while ( PeekMessageA( &msg
, (HWND
)NULL
, 0, 0, PM_REMOVE
) )
215 if ( msg
.message
== WM_QUIT
)
218 if ( msg
.hwnd
!= (HWND
)NULL
)
220 TranslateMessage( &msg
);
221 DispatchMessageA( &msg
);
225 switch ( msg
.message
)
227 case QUARTZ_MSG_ADDTIMER
:
228 case QUARTZ_MSG_REMOVETIMER
:
231 case QUARTZ_MSG_EXITTHREAD
:
235 FIXME( "invalid message %04u\n", (unsigned)msg
.message
);
243 TRACE( "quit thread\n" );
247 /****************************************************************************/
249 static HRESULT WINAPI
250 IReferenceClock_fnQueryInterface(IReferenceClock
* iface
,REFIID riid
,void** ppobj
)
252 CSystemClock_THIS(iface
,refclk
);
254 TRACE("(%p)->()\n",This
);
256 return IUnknown_QueryInterface(This
->unk
.punkControl
,riid
,ppobj
);
260 IReferenceClock_fnAddRef(IReferenceClock
* iface
)
262 CSystemClock_THIS(iface
,refclk
);
264 TRACE("(%p)->()\n",This
);
266 return IUnknown_AddRef(This
->unk
.punkControl
);
270 IReferenceClock_fnRelease(IReferenceClock
* iface
)
272 CSystemClock_THIS(iface
,refclk
);
274 TRACE("(%p)->()\n",This
);
276 return IUnknown_Release(This
->unk
.punkControl
);
279 static HRESULT WINAPI
280 IReferenceClock_fnGetTime(IReferenceClock
* iface
,REFERENCE_TIME
* prtTime
)
282 CSystemClock_THIS(iface
,refclk
);
285 TRACE( "(%p)->(%p)\n", This
, prtTime
);
287 if ( prtTime
== NULL
)
290 EnterCriticalSection( &This
->m_csClock
);
292 dwTimeCur
= GetTickCount();
293 This
->m_rtLast
+= (REFERENCE_TIME
)(DWORD
)(dwTimeCur
- This
->m_dwTimeLast
) * (REFERENCE_TIME
)10000;
295 This
->m_dwTimeLast
= dwTimeCur
;
297 *prtTime
= This
->m_rtLast
;
299 LeaveCriticalSection( &This
->m_csClock
);
304 static HRESULT WINAPI
305 IReferenceClock_fnAdviseTime(IReferenceClock
* iface
,REFERENCE_TIME rtBase
,REFERENCE_TIME rtStream
,HEVENT hEvent
,DWORD_PTR
* pdwAdvCookie
)
307 CSystemClock_THIS(iface
,refclk
);
308 QUARTZ_TimerEntry
* pEntry
;
310 REFERENCE_TIME rtCur
;
312 TRACE( "(%p)->()\n", This
);
314 if ( pdwAdvCookie
== NULL
)
316 if ( hEvent
== (HANDLE
)NULL
)
319 EnterCriticalSection( &This
->m_csClock
);
321 *pdwAdvCookie
= (DWORD_PTR
)(This
->m_dwAdvCookieNext
++);
323 hr
= IReferenceClock_GetTime(iface
,&rtCur
);
326 if ( rtCur
>= (rtBase
+rtStream
) )
333 pEntry
= IReferenceClock_AllocTimerEntry(This
);
334 if ( pEntry
== NULL
)
340 pEntry
->dwAdvCookie
= *pdwAdvCookie
;
341 pEntry
->fPeriodic
= FALSE
;
342 pEntry
->hEvent
= hEvent
;
343 pEntry
->rtStart
= rtBase
;
344 pEntry
->rtInterval
= rtStream
;
346 if ( !PostThreadMessageA(
347 This
->m_idThreadTimer
,
351 pEntry
->hEvent
= (HANDLE
)NULL
;
358 LeaveCriticalSection( &This
->m_csClock
);
363 static HRESULT WINAPI
364 IReferenceClock_fnAdvisePeriodic(IReferenceClock
* iface
,REFERENCE_TIME rtStart
,REFERENCE_TIME rtPeriod
,HSEMAPHORE hSemaphore
,DWORD_PTR
* pdwAdvCookie
)
366 CSystemClock_THIS(iface
,refclk
);
367 QUARTZ_TimerEntry
* pEntry
;
370 TRACE( "(%p)->()\n", This
);
372 if ( pdwAdvCookie
== NULL
)
374 if ( hSemaphore
== (HSEMAPHORE
)NULL
)
377 EnterCriticalSection( &This
->m_csClock
);
379 *pdwAdvCookie
= (DWORD_PTR
)(This
->m_dwAdvCookieNext
++);
381 pEntry
= IReferenceClock_AllocTimerEntry(This
);
382 if ( pEntry
== NULL
)
388 pEntry
->dwAdvCookie
= *pdwAdvCookie
;
389 pEntry
->fPeriodic
= TRUE
;
390 pEntry
->hEvent
= (HANDLE
)hSemaphore
;
391 pEntry
->rtStart
= rtStart
;
392 pEntry
->rtInterval
= rtPeriod
;
394 if ( !PostThreadMessageA(
395 This
->m_idThreadTimer
,
399 pEntry
->hEvent
= (HANDLE
)NULL
;
406 LeaveCriticalSection( &This
->m_csClock
);
411 static HRESULT WINAPI
412 IReferenceClock_fnUnadvise(IReferenceClock
* iface
,DWORD_PTR dwAdvCookie
)
414 CSystemClock_THIS(iface
,refclk
);
415 QUARTZ_TimerEntry
* pEntry
;
417 TRACE( "(%p)->(%lu)\n", This
, (DWORD
)dwAdvCookie
);
419 EnterCriticalSection( &This
->m_csClock
);
421 pEntry
= IReferenceClock_SearchTimer(This
,(DWORD
)dwAdvCookie
);
422 if ( pEntry
!= NULL
)
424 pEntry
->hEvent
= (HANDLE
)NULL
;
427 LeaveCriticalSection( &This
->m_csClock
);
432 static ICOM_VTABLE(IReferenceClock
) irefclk
=
434 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
435 /* IUnknown fields */
436 IReferenceClock_fnQueryInterface
,
437 IReferenceClock_fnAddRef
,
438 IReferenceClock_fnRelease
,
439 /* IReferenceClock fields */
440 IReferenceClock_fnGetTime
,
441 IReferenceClock_fnAdviseTime
,
442 IReferenceClock_fnAdvisePeriodic
,
443 IReferenceClock_fnUnadvise
,
447 HRESULT
CSystemClock_InitIReferenceClock( CSystemClock
* psc
)
452 ICOM_VTBL(&psc
->refclk
) = &irefclk
;
454 InitializeCriticalSection( &psc
->m_csClock
);
455 psc
->m_dwTimeLast
= GetTickCount();
456 psc
->m_rtLast
= (REFERENCE_TIME
)0;
457 psc
->m_hThreadTimer
= (HANDLE
)NULL
;
458 psc
->m_hEventInit
= (HANDLE
)NULL
;
459 psc
->m_idThreadTimer
= 0;
460 psc
->m_dwAdvCookieNext
= 1;
461 ZeroMemory( psc
->m_timerEntries
, sizeof(psc
->m_timerEntries
) );
463 psc
->m_hEventInit
= CreateEventA( NULL
, TRUE
, FALSE
, NULL
);
464 if ( psc
->m_hEventInit
== (HANDLE
)NULL
)
467 psc
->m_hThreadTimer
= CreateThread(
469 IReferenceClock_TimerEntry
,
470 psc
, 0, &psc
->m_idThreadTimer
);
472 if ( psc
->m_hThreadTimer
== (HANDLE
)NULL
)
474 CloseHandle( psc
->m_hEventInit
);
475 psc
->m_hEventInit
= (HANDLE
)NULL
;
479 hEvents
[0] = psc
->m_hEventInit
;
480 hEvents
[1] = psc
->m_hThreadTimer
;
481 if ( WaitForMultipleObjects( 2, hEvents
, FALSE
, INFINITE
)
484 CloseHandle( psc
->m_hEventInit
);
485 psc
->m_hEventInit
= (HANDLE
)NULL
;
486 CloseHandle( psc
->m_hThreadTimer
);
487 psc
->m_hThreadTimer
= (HANDLE
)NULL
;
494 DeleteCriticalSection( &psc
->m_csClock
);
498 void CSystemClock_UninitIReferenceClock( CSystemClock
* psc
)
502 if ( psc
->m_hThreadTimer
!= (HANDLE
)NULL
)
504 if ( PostThreadMessageA(
505 psc
->m_idThreadTimer
,
506 QUARTZ_MSG_EXITTHREAD
,
509 WaitForSingleObject( psc
->m_hThreadTimer
, INFINITE
);
511 CloseHandle( psc
->m_hThreadTimer
);
512 psc
->m_hThreadTimer
= (HANDLE
)NULL
;
515 DeleteCriticalSection( &psc
->m_csClock
);