Kaydet (Commit) 211b3192 authored tarafından Jan Holesovsky's avatar Jan Holesovsky Kaydeden (comit) Michael Meeks

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
üst 08a990fd
...@@ -400,7 +400,19 @@ Time Time::GetUTCOffset() ...@@ -400,7 +400,19 @@ Time Time::GetUTCOffset()
sal_uIntPtr Time::GetSystemTicks() sal_uIntPtr Time::GetSystemTicks()
{ {
#if defined WNT #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 #else
timeval tv; timeval tv;
gettimeofday (&tv, 0); gettimeofday (&tv, 0);
......
...@@ -83,11 +83,11 @@ public: ...@@ -83,11 +83,11 @@ public:
long* mpDitherDiff; // Dither mapping table long* mpDitherDiff; // Dither mapping table
BYTE* mpDitherLow; // Dither mapping table BYTE* mpDitherLow; // Dither mapping table
BYTE* mpDitherHigh; // Dither mapping table BYTE* mpDitherHigh; // Dither mapping table
sal_uLong mnTimerMS; // Current Time (in MS) of the Timer sal_uLong mnTimerMS; // Current Time (in MS) of the Timer
sal_uLong mnTimerOrgMS; // Current Original Time (in MS) sal_uLong mnTimerOrgMS; // Current Original Time (in MS)
DWORD mnNextTimerTime; DWORD mnNextTimerTime;
DWORD mnLastEventTime; DWORD mnLastEventTime;
UINT mnTimerId; // windows timer id HANDLE mnTimerId; ///< Windows timer id
bool mbInTimerProc; // timer event is currently being dispatched bool mbInTimerProc; // timer event is currently being dispatched
HHOOK mhSalObjMsgHook; // hook to get interesting msg for SalObject HHOOK mhSalObjMsgHook; // hook to get interesting msg for SalObject
HWND mhWantLeaveMsg; // window handle, that want a MOUSELEAVE message HWND mhWantLeaveMsg; // window handle, that want a MOUSELEAVE message
...@@ -181,8 +181,7 @@ void ImplSalAcquireYieldMutex( sal_uLong nCount ); ...@@ -181,8 +181,7 @@ void ImplSalAcquireYieldMutex( sal_uLong nCount );
LRESULT CALLBACK SalFrameWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ); LRESULT CALLBACK SalFrameWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
#define SALTIMERPROC_RECURSIVE 0xffffffff void EmitTimerCallback(bool bAllowRecursive);
void CALLBACK SalTimerProc( HWND hWnd, UINT nMsg, UINT_PTR nId, DWORD nTime );
void SalTestMouseLeave(); void SalTestMouseLeave();
bool ImplWriteLastError( DWORD lastError, const char *szApiCall ); bool ImplWriteLastError( DWORD lastError, const char *szApiCall );
...@@ -286,6 +285,9 @@ int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 ); ...@@ -286,6 +285,9 @@ int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 );
// POSTFOCUS-Message; wParam == bFocus; lParam == 0 // POSTFOCUS-Message; wParam == bFocus; lParam == 0
#define SALOBJ_MSG_POSTFOCUS (WM_USER+161) #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 // A/W-Wrapper
BOOL ImplPostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); BOOL ImplPostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
BOOL ImplSendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); BOOL ImplSendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
......
...@@ -736,7 +736,10 @@ LRESULT CALLBACK SalComWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lPar ...@@ -736,7 +736,10 @@ LRESULT CALLBACK SalComWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lPar
rDef = FALSE; rDef = FALSE;
break; break;
case SAL_MSG_POSTTIMER: case SAL_MSG_POSTTIMER:
SalTimerProc( 0, 0, SALTIMERPROC_RECURSIVE, lParam ); EmitTimerCallback(/*bAllowRecursive = */ true);
break;
case SAL_MSG_TIMER_CALLBACK:
EmitTimerCallback(/*bAllowRecursive = */ false);
break; break;
} }
......
...@@ -29,25 +29,37 @@ ...@@ -29,25 +29,37 @@
// maximum period // maximum period
#define MAX_SYSPERIOD 65533 #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 ) void ImplSalStartTimer( sal_uLong nMS, bool bMutex )
{ {
SalData* pSalData = GetSalData(); SalData* pSalData = GetSalData();
// Remember the time of the timer // Remember the time of the timer
pSalData->mnTimerMS = nMS; pSalData->mnTimerMS = nMS;
if ( !bMutex ) if (!bMutex)
pSalData->mnTimerOrgMS = nMS; pSalData->mnTimerOrgMS = nMS;
// duration has to fit into Window's sal_uInt16 // duration has to fit into Window's sal_uInt16
if ( nMS > MAX_SYSPERIOD ) if (nMS > MAX_SYSPERIOD)
nMS = MAX_SYSPERIOD; nMS = MAX_SYSPERIOD;
// kill timer if it exists // change if it exists, create if not
if ( pSalData->mnTimerId ) if (pSalData->mnTimerId)
KillTimer( 0, 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; pSalData->mnNextTimerTime = pSalData->mnLastEventTime + nMS;
} }
...@@ -77,13 +89,17 @@ void WinSalTimer::Stop() ...@@ -77,13 +89,17 @@ void WinSalTimer::Stop()
// If we have a timer, than // If we have a timer, than
if ( pSalData->mnTimerId ) if ( pSalData->mnTimerId )
{ {
KillTimer( 0, pSalData->mnTimerId ); ImplSalStopTimer(pSalData);
pSalData->mnTimerId = 0;
pSalData->mnNextTimerTime = 0; 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 ) #if defined ( __MINGW32__ ) && !defined ( _WIN64 )
jmp_buf jmpbuf; jmp_buf jmpbuf;
...@@ -98,42 +114,58 @@ void CALLBACK SalTimerProc( HWND, UINT, UINT_PTR nId, DWORD ) ...@@ -98,42 +114,58 @@ void CALLBACK SalTimerProc( HWND, UINT, UINT_PTR nId, DWORD )
SalData* pSalData = GetSalData(); SalData* pSalData = GetSalData();
ImplSVData* pSVData = ImplGetSVData(); ImplSVData* pSVData = ImplGetSVData();
// Test for MouseLeave // don't allow recursive calls (mbInTimerProc is set when the callback
SalTestMouseLeave(); // is being processed)
if (pSVData->mpSalTimer && !pSalData->mbInTimerProc)
bool bRecursive = pSalData->mbInTimerProc && (nId != SALTIMERPROC_RECURSIVE);
if ( pSVData->mpSalTimer && ! bRecursive )
{ {
// Try to acquire the mutex. If we don't get the mutex then we ImplPostMessage(pSalData->mpFirstInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK, 0, 0);
// 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 );
} }
}
#if defined ( __MINGW32__ ) && !defined ( _WIN64 ) #if defined ( __MINGW32__ ) && !defined ( _WIN64 )
}
han.Reset(); han.Reset();
#else #else
}
__except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation())) __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
{ {
} }
#endif #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: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment