Kaydet (Commit) 37436815 authored tarafından Jan-Marek Glogowski's avatar Jan-Marek Glogowski

tdf#109123 WIN Run instant timerout with low priority

This busy-lock happens, because user messages have a higher priority
then some system messages. What happens:

1. The main system loop picks up the LO scheduler
2. The idle worker (IW) is started
3. IW checks using AnyInput( VCL_INPUT_ANY ) for system events
4. A system event is found
5. The LO scheduler gets posted again
6. The main system loop picks up the LO scheduler instead of the
   system message => goto 2

Normally it's suggested to use WM_TIMER in this case, as these messages
are supposed to have the lowest priority. But this doesn't work, if
you use PostMessage to generate them and SetTimer doesn't accept a
0ms timeout. At least PeakMessage also picks up the WM_TIMER message
before the system message, probably because PostMessage is somehow
related to the threads queue - who knows.

In the end this implements a manual, low priority event, which is checked
at the end of the ImplSalYield function. It just runs, if there is
nothing else to do. We still have to emit the timer callback event,
as ImplSalYield may wait in GetMessage, but wParam now indicates, if
it's a wakeup and can be ignored. We use the same event, so it's
easier to filter.

Thanks to Mike Kaganski for the missing information and ideas for the
final implementation.

Change-Id: Ib8e4f214ab8d3731d5594d68f38f46982c2eb36d
Reviewed-on: https://gerrit.libreoffice.org/40190Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarJan-Marek Glogowski <glogow@fbihome.de>
üst c73ce1da
...@@ -85,6 +85,7 @@ public: ...@@ -85,6 +85,7 @@ public:
BYTE* mpDitherLow; // Dither mapping table BYTE* mpDitherLow; // Dither mapping table
BYTE* mpDitherHigh; // Dither mapping table BYTE* mpDitherHigh; // Dither mapping table
HANDLE mnTimerId; ///< Windows timer id HANDLE mnTimerId; ///< Windows timer id
bool mbOnIdleRunScheduler; ///< Run the scheduler, if yield is idle
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
AutoTimer* mpMouseLeaveTimer; // Timer for MouseLeave Test AutoTimer* mpMouseLeaveTimer; // Timer for MouseLeave Test
...@@ -274,6 +275,7 @@ int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 ); ...@@ -274,6 +275,7 @@ int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 );
#define SALOBJ_MSG_POSTFOCUS (WM_USER+161) #define SALOBJ_MSG_POSTFOCUS (WM_USER+161)
// Call the Timer's callback from the main thread // Call the Timer's callback from the main thread
// wParam = 1 == run when yield is idle instead of direct
#define SAL_MSG_TIMER_CALLBACK (WM_USER+162) #define SAL_MSG_TIMER_CALLBACK (WM_USER+162)
// Stop the timer from the main thread; wParam = 0, lParam = 0 // Stop the timer from the main thread; wParam = 0, lParam = 0
#define SAL_MSG_STOPTIMER (WM_USER+163) #define SAL_MSG_STOPTIMER (WM_USER+163)
......
...@@ -322,6 +322,7 @@ SalData::SalData() ...@@ -322,6 +322,7 @@ SalData::SalData()
mpDitherLow = nullptr; // Dither mapping table mpDitherLow = nullptr; // Dither mapping table
mpDitherHigh = nullptr; // Dither mapping table mpDitherHigh = nullptr; // Dither mapping table
mnTimerId = nullptr; // windows timer id mnTimerId = nullptr; // windows timer id
mbOnIdleRunScheduler = false; // if yield is idle, run the scheduler
mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
...@@ -565,19 +566,38 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -565,19 +566,38 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
{ {
MSG aMsg; MSG aMsg;
bool bWasMsg = false, bOneEvent = false; bool bWasMsg = false, bOneEvent = false;
SalData *const pSalData = GetSalData();
int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1; int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
do do
{ {
if ( PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE ) ) bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
if ( bOneEvent )
{ {
TranslateMessage( &aMsg ); bWasMsg = true;
ImplSalDispatchMessage( &aMsg ); if ( !(aMsg.message == SAL_MSG_TIMER_CALLBACK && 1 == aMsg.wParam) )
{
bOneEvent = bWasMsg = true; TranslateMessage( &aMsg );
ImplSalDispatchMessage( &aMsg );
}
else
{
// This is just the scheduler wakeup message, in case we're
// waiting in GetMessageW
// So we can just drop it, but we have to fix the accounting!
assert( pSalData->mbOnIdleRunScheduler );
++nMaxEvents;
}
} }
else else
bOneEvent = false; {
if ( nMaxEvents && pSalData->mbOnIdleRunScheduler )
{
pSalData->mbOnIdleRunScheduler = false;
EmitTimerCallback();
bOneEvent = true;
}
}
} while( --nMaxEvents && bOneEvent ); } while( --nMaxEvents && bOneEvent );
// Also check that we don't wait when application already has quit // Also check that we don't wait when application already has quit
...@@ -585,8 +605,12 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -585,8 +605,12 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
{ {
if ( GetMessageW( &aMsg, nullptr, 0, 0 ) ) if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
{ {
TranslateMessage( &aMsg ); // Ignore the scheduler wakeup message
ImplSalDispatchMessage( &aMsg ); if ( !(aMsg.message == SAL_MSG_TIMER_CALLBACK && 1 == aMsg.wParam) )
{
TranslateMessage( &aMsg );
ImplSalDispatchMessage( &aMsg );
}
} }
} }
return bWasMsg; return bWasMsg;
...@@ -708,7 +732,8 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i ...@@ -708,7 +732,8 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
while ( PeekMessageW(&aMsg, nullptr, SAL_MSG_TIMER_CALLBACK, while ( PeekMessageW(&aMsg, nullptr, SAL_MSG_TIMER_CALLBACK,
SAL_MSG_TIMER_CALLBACK, PM_REMOVE) ) SAL_MSG_TIMER_CALLBACK, PM_REMOVE) )
assert( "Multiple timer messages in queue" ); assert( "Multiple timer messages in queue" );
EmitTimerCallback(); if ( 0 == wParam )
EmitTimerCallback();
break; break;
} }
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
#include <win/saltimer.h> #include <win/saltimer.h>
#include <win/salinst.h> #include <win/salinst.h>
void CALLBACK SalTimerProc(PVOID pParameter, BOOLEAN bTimerOrWaitFired); static void CALLBACK SalTimerProc(PVOID pParameter, BOOLEAN bTimerOrWaitFired);
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms687003%28v=vs.85%29.aspx // 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. // (and related pages) for details about the Timer Queues.
...@@ -52,6 +52,7 @@ void ImplSalStopTimer() ...@@ -52,6 +52,7 @@ void ImplSalStopTimer()
SAL_MSG_TIMER_CALLBACK, PM_REMOVE) ) SAL_MSG_TIMER_CALLBACK, PM_REMOVE) )
nMsgCount++; nMsgCount++;
assert( nMsgCount <= 1 ); assert( nMsgCount <= 1 );
pSalData->mbOnIdleRunScheduler = false;
} }
void ImplSalStartTimer( sal_uLong nMS ) void ImplSalStartTimer( sal_uLong nMS )
...@@ -66,11 +67,12 @@ void ImplSalStartTimer( sal_uLong nMS ) ...@@ -66,11 +67,12 @@ void ImplSalStartTimer( sal_uLong nMS )
// cannot change a one-shot timer, so delete it and create a new one // cannot change a one-shot timer, so delete it and create a new one
ImplSalStopTimer(); ImplSalStopTimer();
// directly post a timer callback message for instant timers / idles // run the scheduler, if yield is idle for the 0ms case
if ( 0 == nMS ) pSalData->mbOnIdleRunScheduler = ( 0 == nMS );
if ( pSalData->mbOnIdleRunScheduler )
{ {
BOOL const ret = PostMessageW(pSalData->mpFirstInstance->mhComWnd, BOOL const ret = PostMessageW(pSalData->mpFirstInstance->mhComWnd,
SAL_MSG_TIMER_CALLBACK, 0, 0); SAL_MSG_TIMER_CALLBACK, 1, 0);
SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!"); SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
} }
else else
...@@ -119,7 +121,7 @@ void WinSalTimer::Stop() ...@@ -119,7 +121,7 @@ void WinSalTimer::Stop()
Don't acquire the SolarMutex to avoid deadlocks, just wake up the main thread Don't acquire the SolarMutex to avoid deadlocks, just wake up the main thread
at better resolution than 10ms. at better resolution than 10ms.
*/ */
void CALLBACK SalTimerProc(PVOID, BOOLEAN) static void CALLBACK SalTimerProc(PVOID, BOOLEAN)
{ {
__try __try
{ {
......
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