summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Holesovsky <kendy@collabora.com>2014-09-19 15:48:24 +0200
committerMichael Meeks <michael.meeks@collabora.com>2014-09-23 21:30:47 +0100
commit211b3192f05c4120fa2dd0e23988e74bdd310830 (patch)
tree344aa042f1097d7ef3021afdbf7db5846dc7a419
parent08a990fd27f5a416c2a73902792e93df7499d703 (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.cxx14
-rw-r--r--vcl/inc/win/saldata.hxx12
-rw-r--r--vcl/win/source/app/salinst.cxx5
-rw-r--r--vcl/win/source/app/saltimer.cxx106
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: */