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

WIN another system loop integration attempt

This time we skip the intention to handle our Scheduler
completely via the system event loop. Instead we basically
guarantee to process a Scheduler event during each DoYield,
if one is available. This way we won't block system events
when busy in our event loop.

Change-Id: I37094e61cbf928733151d9cc3299cdac76b3a5cd
Reviewed-on: https://gerrit.libreoffice.org/43349Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarJan-Marek Glogowski <glogow@fbihome.de>
üst 21ccc647
...@@ -180,10 +180,12 @@ based on the function used to generate them. Even if WM_TIMER messages should ...@@ -180,10 +180,12 @@ based on the function used to generate them. Even if WM_TIMER messages should
have the lowest priority, a manually posted WM_TIMER is processed with the have the lowest priority, a manually posted WM_TIMER is processed with the
priority of a PostMessage message. priority of a PostMessage message.
Therefore the current solution always starts a (threaded) timer, even for the So we're giving up on processing all our Scheduler events as a message in the
instant Idles, and syncs to this timer message in the main dispatch loop, to system message loop. Instead we just indicate a 0ms timer message by setting
process the messages in the correct sequence. Using SwitchToThread(), this the m_bDirectTimeout in the timer object. This timer is always processed, if
kind of polling seem to work reasonably well. the system message wasn't already our timer. As a result we can also skip the
polling. All this is one more reason to drop the single message processing
in favour of always processing all pending (system) events.
An additional workaround is implemented for the delayed queuing of posted An additional workaround is implemented for the delayed queuing of posted
messages, where PeekMessage in WinSalTimer::Stop() won't be able remove the messages, where PeekMessage in WinSalTimer::Stop() won't be able remove the
......
...@@ -26,15 +26,18 @@ class WinSalTimer final : public SalTimer, protected VersionedEvent ...@@ -26,15 +26,18 @@ class WinSalTimer final : public SalTimer, protected VersionedEvent
{ {
// for access to Impl* functions // for access to Impl* functions
friend LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, int& rDef ); friend LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, int& rDef );
// for access to m_bPollForMessage // for access to GetNextVersionedEvent
friend void CALLBACK SalTimerProc( PVOID data, BOOLEAN ); friend void CALLBACK SalTimerProc( PVOID data, BOOLEAN );
// for access to ImplHandleElapsedTimer
friend bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents );
HANDLE m_nTimerId; ///< Windows timer id HANDLE m_nTimerId; ///< Windows timer id
bool m_bPollForMessage; ///< Run yield until a message is caught (most likely the 0ms timer) bool m_bDirectTimeout; ///< timeout can be processed directly
void ImplStart( sal_uIntPtr nMS ); void ImplStart( sal_uIntPtr nMS );
void ImplStop(); void ImplStop();
void ImplEmitTimerCallback(); void ImplHandleTimerEvent( WPARAM aWPARAM );
void ImplHandleElapsedTimer();
public: public:
WinSalTimer(); WinSalTimer();
...@@ -43,19 +46,12 @@ public: ...@@ -43,19 +46,12 @@ public:
virtual void Start(sal_uIntPtr nMS) override; virtual void Start(sal_uIntPtr nMS) override;
virtual void Stop() override; virtual void Stop() override;
inline bool IsValidWPARAM( WPARAM wParam ) const; inline bool IsDirectTimeout() const;
inline bool PollForMessage() const;
}; };
inline bool WinSalTimer::IsValidWPARAM( WPARAM aWPARAM ) const inline bool WinSalTimer::IsDirectTimeout() const
{
return IsValidEventVersion( static_cast<sal_Int32>( aWPARAM ) );
}
inline bool WinSalTimer::PollForMessage() const
{ {
return m_bPollForMessage; return m_bDirectTimeout;
} }
#endif #endif
......
...@@ -472,11 +472,11 @@ static void ImplSalDispatchMessage( MSG* pMsg ) ...@@ -472,11 +472,11 @@ static void ImplSalDispatchMessage( MSG* pMsg )
ImplSalPostDispatchMsg( pMsg, lResult ); ImplSalPostDispatchMsg( pMsg, lResult );
} }
static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents ) bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
{ {
static sal_uInt32 nLastTicks = 0; static sal_uInt32 nLastTicks = 0;
MSG aMsg; MSG aMsg;
bool bWasMsg = false, bOneEvent = false; bool bWasMsg = false, bOneEvent = false, bWasTimeoutMsg = false;
ImplSVData *const pSVData = ImplGetSVData(); ImplSVData *const pSVData = ImplGetSVData();
WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer ); WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
...@@ -491,6 +491,8 @@ static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -491,6 +491,8 @@ static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
if ( bOneEvent ) if ( bOneEvent )
{ {
bWasMsg = true; bWasMsg = true;
if ( !bWasTimeoutMsg )
bWasTimeoutMsg = (SAL_MSG_TIMER_CALLBACK == aMsg.message);
TranslateMessage( &aMsg ); TranslateMessage( &aMsg );
ImplSalDispatchMessage( &aMsg ); ImplSalDispatchMessage( &aMsg );
if ( bHandleAllCurrentEvents if ( bHandleAllCurrentEvents
...@@ -499,23 +501,26 @@ static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -499,23 +501,26 @@ static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
bHadNewerEvent = true; bHadNewerEvent = true;
bOneEvent = !bHadNewerEvent; bOneEvent = !bHadNewerEvent;
} }
// busy loop to catch a message, eventually the 0ms timer.
// we don't need to loop, if we wait anyway.
if ( !bWait && !bWasMsg && pTimer && pTimer->PollForMessage() )
{
SwitchToThread();
continue;
}
if ( !(bHandleAllCurrentEvents && bOneEvent) ) if ( !(bHandleAllCurrentEvents && bOneEvent) )
break; break;
} }
while( true ); while( true );
// 0ms timeouts are handled out-of-bounds to prevent busy-locking the
// event loop with timeout massages.
// We ensure we never handle more then one timeout per call.
// This way we'll always process a normal system message.
if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
{
pTimer->ImplHandleElapsedTimer();
bWasMsg = true;
}
if ( bHandleAllCurrentEvents ) if ( bHandleAllCurrentEvents )
nLastTicks = nCurTicks; nLastTicks = nCurTicks;
// Also check that we don't wait when application already has quit if ( bWait && !bWasMsg )
if ( bWait && !bWasMsg && !pSVData->maAppData.mbAppQuit )
{ {
if ( GetMessageW( &aMsg, nullptr, 0, 0 ) ) if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
{ {
...@@ -540,8 +545,6 @@ bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents) ...@@ -540,8 +545,6 @@ bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
SolarMutexReleaser aReleaser; SolarMutexReleaser aReleaser;
if ( !IsMainThread() ) if ( !IsMainThread() )
{ {
// If you change the SendMessageW function, you might need to update
// the PeekMessage( ... PM_QS_POSTMESSAGE) calls!
bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD, bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
(WPARAM) false, (LPARAM) bHandleAllCurrentEvents ); (WPARAM) false, (LPARAM) bHandleAllCurrentEvents );
if ( !bDidWork && bWait ) if ( !bDidWork && bWait )
...@@ -659,17 +662,7 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i ...@@ -659,17 +662,7 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
{ {
WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer ); WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
assert( pTimer != nullptr ); assert( pTimer != nullptr );
MSG aMsg; pTimer->ImplHandleTimerEvent( wParam );
bool bValidMSG = pTimer->IsValidWPARAM( wParam );
// PM_QS_POSTMESSAGE is needed, so we don't process the SendMessage from DoYield!
while ( PeekMessageW(&aMsg, pInst->mhComWnd, SAL_MSG_TIMER_CALLBACK,
SAL_MSG_TIMER_CALLBACK, PM_REMOVE | PM_NOYIELD | PM_QS_POSTMESSAGE) )
{
assert( !bValidMSG && "Unexpected non-last valid message" );
bValidMSG = pTimer->IsValidWPARAM( aMsg.wParam );
}
if ( bValidMSG )
pTimer->ImplEmitTimerCallback();
break; break;
} }
} }
......
...@@ -46,16 +46,11 @@ void WinSalTimer::ImplStop() ...@@ -46,16 +46,11 @@ void WinSalTimer::ImplStop()
return; return;
m_nTimerId = nullptr; m_nTimerId = nullptr;
m_bDirectTimeout = false;
DeleteTimerQueueTimer( nullptr, hTimer, INVALID_HANDLE_VALUE ); DeleteTimerQueueTimer( nullptr, hTimer, INVALID_HANDLE_VALUE );
// Keep both after DeleteTimerQueueTimer, because they are set in SalTimerProc // Keep InvalidateEvent after DeleteTimerQueueTimer, because the event id
// is set in SalTimerProc, which DeleteTimerQueueTimer will finish or cancel.
InvalidateEvent(); InvalidateEvent();
m_bPollForMessage = false;
// remove as many pending SAL_MSG_TIMER_CALLBACK messages as possible
// PM_QS_POSTMESSAGE is needed, so we don't process the SendMessage from DoYield!
MSG aMsg;
while ( PeekMessageW(&aMsg, pInst->mhComWnd, SAL_MSG_TIMER_CALLBACK,
SAL_MSG_TIMER_CALLBACK, PM_REMOVE | PM_NOYIELD | PM_QS_POSTMESSAGE) );
} }
void WinSalTimer::ImplStart( sal_uLong nMS ) void WinSalTimer::ImplStart( sal_uLong nMS )
...@@ -70,20 +65,21 @@ void WinSalTimer::ImplStart( sal_uLong nMS ) ...@@ -70,20 +65,21 @@ void WinSalTimer::ImplStart( 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
ImplStop(); ImplStop();
// keep the yield running, if a 0ms Idle is scheduled // directly indicate an elapsed timer
m_bPollForMessage = ( 0 == nMS ); m_bDirectTimeout = ( 0 == nMS );
// probably WT_EXECUTEONLYONCE is not needed, but it enforces Period // probably WT_EXECUTEONLYONCE is not needed, but it enforces Period
// to be 0 and should not hurt; also see // to be 0 and should not hurt; also see
// https://www.microsoft.com/msj/0499/pooling/pooling.aspx // https://www.microsoft.com/msj/0499/pooling/pooling.aspx
CreateTimerQueueTimer(&m_nTimerId, nullptr, SalTimerProc, if ( !m_bDirectTimeout )
reinterpret_cast<void*>( CreateTimerQueueTimer(&m_nTimerId, nullptr, SalTimerProc, this,
sal_IntPtr(GetNextEventVersion())), nMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
nMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE); // We don't need any wakeup message, as this code can just run in the
// main thread!
} }
WinSalTimer::WinSalTimer() WinSalTimer::WinSalTimer()
: m_nTimerId( nullptr ) : m_nTimerId( nullptr )
, m_bPollForMessage( false ) , m_bDirectTimeout( false )
{ {
} }
...@@ -126,12 +122,10 @@ void CALLBACK SalTimerProc(PVOID data, BOOLEAN) ...@@ -126,12 +122,10 @@ void CALLBACK SalTimerProc(PVOID data, BOOLEAN)
{ {
__try __try
{ {
// always post message when the timer fires, we will remove the ones WinSalTimer *pTimer = reinterpret_cast<WinSalTimer*>( data );
// that happened during execution of the callback later directly from BOOL const ret = PostMessageW(
// the message queue GetSalData()->mpInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK,
BOOL const ret = PostMessageW(GetSalData()->mpInstance->mhComWnd, static_cast<WPARAM>(pTimer->GetNextEventVersion()), 0 );
SAL_MSG_TIMER_CALLBACK,
reinterpret_cast<WPARAM>(data), 0);
#if OSL_DEBUG_LEVEL > 0 #if OSL_DEBUG_LEVEL > 0
if (0 == ret) // SEH prevents using SAL_WARN here? if (0 == ret) // SEH prevents using SAL_WARN here?
fputs("ERROR: PostMessage() failed!\n", stderr); fputs("ERROR: PostMessage() failed!\n", stderr);
...@@ -142,15 +136,24 @@ void CALLBACK SalTimerProc(PVOID data, BOOLEAN) ...@@ -142,15 +136,24 @@ void CALLBACK SalTimerProc(PVOID data, BOOLEAN)
} }
} }
void WinSalTimer::ImplEmitTimerCallback() void WinSalTimer::ImplHandleElapsedTimer()
{ {
// Test for MouseLeave // Test for MouseLeave
SalTestMouseLeave(); SalTestMouseLeave();
m_bPollForMessage = false; m_bDirectTimeout = false;
ImplSalYieldMutexAcquireWithWait(); ImplSalYieldMutexAcquireWithWait();
CallCallback(); CallCallback();
ImplSalYieldMutexRelease(); ImplSalYieldMutexRelease();
} }
void WinSalTimer::ImplHandleTimerEvent( const WPARAM aWPARAM )
{
assert( aWPARAM <= SAL_MAX_INT32 );
if ( !IsValidEventVersion( static_cast<sal_Int32>( aWPARAM ) ) )
return;
ImplHandleElapsedTimer();
}
/* 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