diff options
author | Jan Holesovsky <kendy@collabora.com> | 2014-09-19 15:48:24 +0200 |
---|---|---|
committer | Michael Meeks <michael.meeks@collabora.com> | 2014-09-23 21:30:47 +0100 |
commit | 211b3192f05c4120fa2dd0e23988e74bdd310830 (patch) | |
tree | 344aa042f1097d7ef3021afdbf7db5846dc7a419 | |
parent | 08a990fd27f5a416c2a73902792e93df7499d703 (diff) |
fdo#84000: Reimplement the Windows WinSalTimer using Timer Queues.
Timer Queues
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686796%28v=vs.85%29.aspx
allow creating & maintaing high-precision timers. This commit switches the
WinSalTimer implementation from using the Timers:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644900%28v=vs.85%29.aspx
to Timer Queue Timers.
The 'classic' Timers do not have better precision than some 15.6ms (the
documentation mentions 10ms, but some measuring seems to confirm that it is
more than that).
With the Timer Queue Timers, we now have 1ms precision.
Incorporates some cleanup from Michael Meeks <michael.meeks@collabora.com>.
Change-Id: I0312a0c9fdc2779258698b24389b24c39e643473
-rw-r--r-- | tools/source/datetime/ttime.cxx | 14 | ||||
-rw-r--r-- | vcl/inc/win/saldata.hxx | 12 | ||||
-rw-r--r-- | vcl/win/source/app/salinst.cxx | 5 | ||||
-rw-r--r-- | vcl/win/source/app/saltimer.cxx | 106 |
4 files changed, 93 insertions, 44 deletions
diff --git a/tools/source/datetime/ttime.cxx b/tools/source/datetime/ttime.cxx index 0b147b443c33..71836e721689 100644 --- a/tools/source/datetime/ttime.cxx +++ b/tools/source/datetime/ttime.cxx @@ -400,7 +400,19 @@ Time Time::GetUTCOffset() sal_uIntPtr Time::GetSystemTicks() { #if defined WNT - return (sal_uIntPtr)GetTickCount(); + static LARGE_INTEGER nTicksPerMS; + static bool bTicksPerMSInitialized = false; + if (!bTicksPerMSInitialized) + { + QueryPerformanceFrequency(&nTicksPerMS); + nTicksPerMS.QuadPart /= 1000; + bTicksPerMSInitialized = true; + } + + LARGE_INTEGER nPerformanceCount; + QueryPerformanceCounter(&nPerformanceCount); + + return (sal_uIntPtr)(nPerformanceCount.QuadPart/nTicksPerMS.QuadPart); #else timeval tv; gettimeofday (&tv, 0); diff --git a/vcl/inc/win/saldata.hxx b/vcl/inc/win/saldata.hxx index d047fc54a857..409ca3eb13e6 100644 --- a/vcl/inc/win/saldata.hxx +++ b/vcl/inc/win/saldata.hxx @@ -83,11 +83,11 @@ public: long* mpDitherDiff; // Dither mapping table BYTE* mpDitherLow; // Dither mapping table BYTE* mpDitherHigh; // Dither mapping table - sal_uLong mnTimerMS; // Current Time (in MS) of the Timer - sal_uLong mnTimerOrgMS; // Current Original Time (in MS) + sal_uLong mnTimerMS; // Current Time (in MS) of the Timer + sal_uLong mnTimerOrgMS; // Current Original Time (in MS) DWORD mnNextTimerTime; DWORD mnLastEventTime; - UINT mnTimerId; // windows timer id + HANDLE mnTimerId; ///< Windows timer id bool mbInTimerProc; // timer event is currently being dispatched HHOOK mhSalObjMsgHook; // hook to get interesting msg for SalObject HWND mhWantLeaveMsg; // window handle, that want a MOUSELEAVE message @@ -181,8 +181,7 @@ void ImplSalAcquireYieldMutex( sal_uLong nCount ); LRESULT CALLBACK SalFrameWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ); -#define SALTIMERPROC_RECURSIVE 0xffffffff -void CALLBACK SalTimerProc( HWND hWnd, UINT nMsg, UINT_PTR nId, DWORD nTime ); +void EmitTimerCallback(bool bAllowRecursive); void SalTestMouseLeave(); bool ImplWriteLastError( DWORD lastError, const char *szApiCall ); @@ -286,6 +285,9 @@ int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 ); // POSTFOCUS-Message; wParam == bFocus; lParam == 0 #define SALOBJ_MSG_POSTFOCUS (WM_USER+161) +// Call the Timer's callback from the main thread +#define SAL_MSG_TIMER_CALLBACK (WM_USER+162) + // A/W-Wrapper BOOL ImplPostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); BOOL ImplSendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); diff --git a/vcl/win/source/app/salinst.cxx b/vcl/win/source/app/salinst.cxx index 7a697ae093dd..c8eb1106315e 100644 --- a/vcl/win/source/app/salinst.cxx +++ b/vcl/win/source/app/salinst.cxx @@ -736,7 +736,10 @@ LRESULT CALLBACK SalComWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lPar rDef = FALSE; break; case SAL_MSG_POSTTIMER: - SalTimerProc( 0, 0, SALTIMERPROC_RECURSIVE, lParam ); + EmitTimerCallback(/*bAllowRecursive = */ true); + break; + case SAL_MSG_TIMER_CALLBACK: + EmitTimerCallback(/*bAllowRecursive = */ false); break; } diff --git a/vcl/win/source/app/saltimer.cxx b/vcl/win/source/app/saltimer.cxx index 287ec9addcfc..779c6918647b 100644 --- a/vcl/win/source/app/saltimer.cxx +++ b/vcl/win/source/app/saltimer.cxx @@ -29,25 +29,37 @@ // maximum period #define MAX_SYSPERIOD 65533 +void CALLBACK SalTimerProc(PVOID pParameter, BOOLEAN bTimerOrWaitFired); + +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms687003%28v=vs.85%29.aspx +// (and related pages) for details about the Timer Queues. + +void ImplSalStopTimer(SalData* pSalData) +{ + HANDLE hTimer = pSalData->mnTimerId; + pSalData->mnTimerId = 0; + DeleteTimerQueueTimer(NULL, hTimer, 0); +} + void ImplSalStartTimer( sal_uLong nMS, bool bMutex ) { SalData* pSalData = GetSalData(); // Remember the time of the timer pSalData->mnTimerMS = nMS; - if ( !bMutex ) + if (!bMutex) pSalData->mnTimerOrgMS = nMS; // duration has to fit into Window's sal_uInt16 - if ( nMS > MAX_SYSPERIOD ) + if (nMS > MAX_SYSPERIOD) nMS = MAX_SYSPERIOD; - // kill timer if it exists - if ( pSalData->mnTimerId ) - KillTimer( 0, pSalData->mnTimerId ); + // change if it exists, create if not + if (pSalData->mnTimerId) + ChangeTimerQueueTimer(NULL, pSalData->mnTimerId, nMS, nMS); + else + CreateTimerQueueTimer(&pSalData->mnTimerId, NULL, SalTimerProc, NULL, nMS, nMS, WT_EXECUTEDEFAULT); - // Make a new timer with new period - pSalData->mnTimerId = SetTimer( 0, 0, (UINT)nMS, SalTimerProc ); pSalData->mnNextTimerTime = pSalData->mnLastEventTime + nMS; } @@ -77,13 +89,17 @@ void WinSalTimer::Stop() // If we have a timer, than if ( pSalData->mnTimerId ) { - KillTimer( 0, pSalData->mnTimerId ); - pSalData->mnTimerId = 0; + ImplSalStopTimer(pSalData); pSalData->mnNextTimerTime = 0; } } -void CALLBACK SalTimerProc( HWND, UINT, UINT_PTR nId, DWORD ) +/** This gets invoked from a Timer Queue thread. + +Don't acquire the SolarMutex to avoid deadlocks, just wake up the main thread +at better resolution than 10ms. +*/ +void CALLBACK SalTimerProc(PVOID, BOOLEAN) { #if defined ( __MINGW32__ ) && !defined ( _WIN64 ) jmp_buf jmpbuf; @@ -98,42 +114,58 @@ void CALLBACK SalTimerProc( HWND, UINT, UINT_PTR nId, DWORD ) SalData* pSalData = GetSalData(); ImplSVData* pSVData = ImplGetSVData(); - // Test for MouseLeave - SalTestMouseLeave(); - - bool bRecursive = pSalData->mbInTimerProc && (nId != SALTIMERPROC_RECURSIVE); - if ( pSVData->mpSalTimer && ! bRecursive ) + // don't allow recursive calls (mbInTimerProc is set when the callback + // is being processed) + if (pSVData->mpSalTimer && !pSalData->mbInTimerProc) { - // Try to acquire the mutex. If we don't get the mutex then we - // try this a short time later again. - if ( ImplSalYieldMutexTryToAcquire() ) - { - bRecursive = pSalData->mbInTimerProc && (nId != SALTIMERPROC_RECURSIVE); - if ( pSVData->mpSalTimer && ! bRecursive ) - { - pSalData->mbInTimerProc = TRUE; - pSVData->mpSalTimer->CallCallback(); - pSalData->mbInTimerProc = FALSE; - ImplSalYieldMutexRelease(); - - // Run the timer in the correct time, if we start this - // with a small timeout, because we don't get the mutex - if ( pSalData->mnTimerId && - (pSalData->mnTimerMS != pSalData->mnTimerOrgMS) ) - ImplSalStartTimer( pSalData->mnTimerOrgMS, FALSE ); - } - } - else - ImplSalStartTimer( 10, TRUE ); + ImplPostMessage(pSalData->mpFirstInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK, 0, 0); } - } #if defined ( __MINGW32__ ) && !defined ( _WIN64 ) + } han.Reset(); #else + } __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation())) { } #endif } +/** Called in the main thread. + +We assured that by posting the message from the SalTimeProc only, the real +call then happens when the main thread gets SAL_MSG_TIMER_CALLBACK. + +@param bAllowRecursive allows to skip the check that assures that two timeouts +do not overlap. +*/ +void EmitTimerCallback(bool bAllowRecursive) +{ + SalData* pSalData = GetSalData(); + ImplSVData* pSVData = ImplGetSVData(); + + // Test for MouseLeave + SalTestMouseLeave(); + + // Try to acquire the mutex. If we don't get the mutex then we + // try this a short time later again. + if (ImplSalYieldMutexTryToAcquire()) + { + if (pSVData->mpSalTimer && (!pSalData->mbInTimerProc || bAllowRecursive)) + { + pSalData->mbInTimerProc = true; + pSVData->mpSalTimer->CallCallback(); + pSalData->mbInTimerProc = false; + ImplSalYieldMutexRelease(); + + // Run the timer in the correct time, if we start this + // with a small timeout, because we don't get the mutex + if (pSalData->mnTimerId && (pSalData->mnTimerMS != pSalData->mnTimerOrgMS)) + ImplSalStartTimer(pSalData->mnTimerOrgMS, false); + } + } + else + ImplSalStartTimer(10, true); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |