[Libreoffice-commits] core.git: Branch 'private/jmux/scheduler-fixes' - 248 commits - android/Bootstrap android/source avmedia/source basctl/source basic/source canvas/Library_canvastools.mk canvas/source chart2/source comphelper/source compilerplugins/clang configmgr/source connectivity/source connectivity/workben cppuhelper/source cppu/source cui/source dbaccess/Module_dbaccess.mk dbaccess/source desktop/source desktop/StaticLibrary_minidump.mk desktop/win32 download.lst drawinglayer/source dtrans/source editeng/source embeddedobj/source embedserv/source emfio/source extensions/source external/apache-commons external/beanshell external/hsqldb external/jfreereport external/languagetool external/nss external/owncloud-android-lib external/pdfium external/rhino extras/source filter/source forms/source fpicker/source fpicker/uiconfig framework/inc framework/qa framework/source helpcontent2 hwpfilter/source icon-themes/breeze icon-themes/breeze_dark icon-themes/breeze_svg icon-themes/sifr icon-themes /sifr_dark icon-themes/sifr_svg icon-themes/tango idlc/source include/avmedia include/basic include/comphelper include/editeng include/filter include/o3tl include/oox include/osl include/sal include/sax include/sfx2 include/sot include/svl include/svtools include/svx include/toolkit include/tools include/vbahelper include/vcl include/xmloff instsetoo_native/CustomTarget_setup.mk io/source javaunohelper/test jurt/com l10ntools/inc lotuswordpro/source mysqlc/source odk/CustomTarget_build-examples.mk odk/examples odk/source offapi/com officecfg/registry oox/source opencl/source package/source reportdesign/source sal/Library_sal.mk sal/osl sal/rtl sal/util sc/CppunitTest_sc_subsequent_filters_test.mk sc/inc scp2/source sc/qa scripting/source sc/source sc/uiconfig sd/CppunitTest_sd_import_tests_smartart.mk sdext/source sd/Module_sd.mk sd/qa sd/source setup_native/source sfx2/source shell/source smoketest/libtest.cxx solenv/bin solenv/CompilerTest_compilerplugins_clang.mk sot/source starm ath/source stoc/source svl/qa svl/source svtools/source svx/Library_svx.mk svx/source svx/uiconfig sw/inc sw/qa sw/README sw/source sw/uiconfig test/source toolkit/qa toolkit/source tools/source ucbhelper/source ucb/qa ucb/source udkapi/com UnoControls/source unotools/source uui/source vbahelper/source vcl/android vcl/headless vcl/inc vcl/ios vcl/Library_vcl.mk vcl/null vcl/opengl vcl/osx vcl/qa vcl/quartz vcl/README.scheduler vcl/source vcl/unx vcl/win winaccessibility/inc winaccessibility/source writerfilter/source writerperfect/qa writerperfect/source xmlhelp/source xmloff/source xmlsecurity/inc xmlsecurity/source xmlsecurity/uiconfig
Jan-Marek Glogowski
glogow at fbihome.de
Mon Sep 4 16:17:28 UTC 2017
Rebased ref, commits from common ancestor:
commit 969e9337f010ac567ec50d138990e7644fdbf4cd
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Mon Sep 4 17:40:13 2017 +0200
fixup
Change-Id: I67957742ab7795856e2b028ee214febe965a5ed2
diff --git a/vcl/headless/svpinst.cxx b/vcl/headless/svpinst.cxx
index 8a805ea345b1..5fcb7c57d602 100644
--- a/vcl/headless/svpinst.cxx
+++ b/vcl/headless/svpinst.cxx
@@ -306,29 +306,27 @@ SalBitmap* SvpSalInstance::CreateSalBitmap()
bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
- // first, check for already queued events.
- std::list< SalUserEvent > aEvents;
+ bool bEvent = false;
+
+ // first, process current user events
+ std::list< SalUserEvent > aCurrentEvents;
{
osl::MutexGuard g(m_aEventGuard);
if( ! m_aUserEvents.empty() )
{
if( bHandleAllCurrentEvents )
- {
- aEvents = m_aUserEvents;
- m_aUserEvents.clear();
- }
+ aCurrentEvents.swap( m_aUserEvents );
else
{
- aEvents.push_back( m_aUserEvents.front() );
+ aCurrentEvents.push_back( m_aUserEvents.front() );
m_aUserEvents.pop_front();
}
+ bEvent = true;
}
}
-
- bool bEvent = !aEvents.empty();
if( bEvent )
{
- for( std::list<SalUserEvent>::const_iterator it = aEvents.begin(); it != aEvents.end(); ++it )
+ for( auto it = aCurrentEvents.begin(); it != aCurrentEvents.end(); ++it )
{
if ( isFrameAlive( it->m_pFrame ) )
{
@@ -340,6 +338,9 @@ bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
pSvpFrame->PostPaint();
}
}
+
+ if ( !bHandleAllCurrentEvents )
+ return true;
}
}
diff --git a/vcl/inc/osx/salinst.h b/vcl/inc/osx/salinst.h
index 207f6cce7b3c..028b02afe630 100644
--- a/vcl/inc/osx/salinst.h
+++ b/vcl/inc/osx/salinst.h
@@ -143,9 +143,7 @@ public:
// this is needed to avoid duplicate open events through a) command line and b) NSApp's openFile
static bool isOnCommandLine( const OUString& );
- void wakeupYield();
-
- public:
+public:
friend class AquaSalFrame;
void PostUserEvent( AquaSalFrame* pFrame, SalEvent nType, void* pData );
@@ -163,7 +161,6 @@ public:
static const short AppStartTimerEvent = 10;
static const short YieldWakeupEvent = 20;
static const short DispatchTimerEvent = 30;
- static const short PostedUserEvent = 40;
static NSMenu* GetDynamicDockMenu();
};
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index b663227cf244..c037c9cf5ddf 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -392,21 +392,16 @@ AquaSalInstance::~AquaSalInstance()
delete mpSalYieldMutex;
}
-void AquaSalInstance::wakeupYield()
-{
- // wakeup :Yield
- if( mbWaitingYield )
- ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, YES );
-}
-
void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, SalEvent nType, void* pData )
{
{
osl::MutexGuard g( maUserEventListMutex );
maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) );
}
- // notify main loop that an event has arrived
- wakeupYield();
+ if( mbWaitingYield )
+ dispatch_async(dispatch_get_main_queue(),^{
+ ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO );
+ });
}
comphelper::SolarMutex* AquaSalInstance::GetYieldMutex()
@@ -457,9 +452,6 @@ void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
case DispatchTimerEvent:
AquaSalTimer::handleDispatchTimerEvent();
break;
- case YieldWakeupEvent:
- // do nothing
- break;
#if !HAVE_FEATURE_MACOSX_SANDBOX
case AppleRemoteControlEvent: // Defined in <apple_remote/RemoteMainController.h>
{
@@ -545,8 +537,6 @@ bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents )
bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
- bool bHadEvent = false;
-
// ensure that the per thread autorelease pool is top level and
// will therefore not be destroyed by cocoa implicitly
SalData::ensureThreadAutoreleasePool();
@@ -555,35 +545,39 @@ bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
// an own pool for each yield level
ReleasePoolHolder aReleasePool;
- // Release all locks so that we don't deadlock when we pull pending
- // events from the event queue
- bool bDispatchUser = true;
- while( bDispatchUser )
+ bool bHadEvent = false;
+
+ // first, process current user events
+ std::list< SalUserEvent > aCurrentEvents;
{
- // get one user event
- SalUserEvent aEvent( nullptr, nullptr, SalEvent::NONE );
+ osl::MutexGuard g(m_aEventGuard);
+ if( ! m_aUserEvents.empty() )
{
- osl::MutexGuard g( maUserEventListMutex );
- if( ! maUserEvents.empty() )
+ if( bHandleAllCurrentEvents )
+ aCurrentEvents.swap( m_aUserEvents );
+ else
{
- aEvent = maUserEvents.front();
- maUserEvents.pop_front();
- bHadEvent = true;
+ aCurrentEvents.push_back( m_aUserEvents.front() );
+ m_aUserEvents.pop_front();
}
- else
- bDispatchUser = false;
+ bHadEvent = true;
}
-
- // dispatch it
- if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) )
+ }
+ if ( bHadEvent )
+ {
+ for( auto it = aCurrentEvents.begin(); it != aCurrentEvents.end(); ++it )
{
- aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData );
- maWaitingYieldCond.set();
- }
+ // dispatch it
+ if( it->mpFrame && AquaSalFrame::isAlive( it->mpFrame ) )
+ {
+ aEvent.mpFrame->CallCallback( it->mnType, it->mpData );
+ maWaitingYieldCond.set();
+ }
- // return if only one event is asked for
- if( !bHandleAllCurrentEvents && bDispatchUser )
- return true;
+ // return if only one event is asked for
+ if( !bHandleAllCurrentEvents )
+ return true;
+ }
}
// handle cocoa event queue
commit 41fec94c735ecca9abca114cd60bf6fb6360b46e
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Tue Aug 29 10:29:51 2017 +0200
Unify Reschedule implementation and usage
Application::Reschedule(true) should process just all currently
pending events and ignore all newly generated events when
processing them.
This way we also can drop nMaxEvents from the Windows backend. This
limit was also never implemented on OSX and for the KDE4 backend
it's actually impossible to handle single events.
Also changes various call sites to just process all pending events
instead of some made of number of times.
Change-Id: I1ab95df89b079cc8c6319a808194fe3127144d1c
diff --git a/cui/source/dialogs/cuigaldlg.cxx b/cui/source/dialogs/cuigaldlg.cxx
index 599ec984b70f..598496f1d0e5 100644
--- a/cui/source/dialogs/cuigaldlg.cxx
+++ b/cui/source/dialogs/cuigaldlg.cxx
@@ -483,8 +483,7 @@ IMPL_LINK( ActualizeProgress, TimeoutHdl, Timer*, _pTimer, void)
IMPL_LINK( ActualizeProgress, ActualizeHdl, const INetURLObject&, rURL, void )
{
- for( long i = 0; i < 128; i++ )
- Application::Reschedule();
+ Application::Reschedule( true );
Flush();
diff --git a/include/vcl/scheduler.hxx b/include/vcl/scheduler.hxx
index 5c9d8f0d0d6e..2d422c6e9678 100644
--- a/include/vcl/scheduler.hxx
+++ b/include/vcl/scheduler.hxx
@@ -53,7 +53,16 @@ public:
static void CallbackTaskScheduling();
/// Process one pending task ahead of time with highest priority.
static bool ProcessTaskScheduling();
- /// Process all events until we are idle
+ /**
+ * Process all events until none is pending
+ *
+ * This can busy-lock, if some task or system event always generates new
+ * events when being processed. Most time it's called in unit tests to
+ * process all pending events. Internally it just calls
+ * Application::Reschedule( true ) until it fails.
+ *
+ * @see Application::Reschedule
+ */
static void ProcessEventsToIdle();
/**
* Process events until the parameter turns true,
diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx
index d6e186350424..cdb3c2c150ac 100644
--- a/include/vcl/svapp.hxx
+++ b/include/vcl/svapp.hxx
@@ -466,17 +466,19 @@ public:
/** Attempt to process current pending event(s)
It doesn't sleep if no events are available for processing.
+ This doesn't processs any events generated after invoking the function.
+ So in contrast to Scheduler::ProcessEventsToIdle, this cannot be
+ dead-locked by event loops.
- @param bAllEvents If set to true, then try to process all the
- events. If set to false, then only process the current
- event. Defaults to false.
+ @param bHandleAllCurrentEvents If set to true, then try to process all
+ the current events. If set to false, then only process one event.
+ Defaults to false.
@returns true if any event was processed.
- @see Execute, Quit, Yield, EndYield, GetSolarMutex,
- GetMainThreadIdentifier, ReleaseSolarMutex, AcquireSolarMutex,
+ @see Yield, Scheduler::ProcessEventsToIdle
*/
- static bool Reschedule( bool bAllEvents = false );
+ static bool Reschedule( bool bHandleAllCurrentEvents = false );
/** Process the next event.
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index fd9c71197ded..1f56bba58179 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -1400,7 +1400,6 @@ void ScTiledRenderingTest::testDisableUndoRepair()
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
Scheduler::ProcessEventsToIdle();
- Scheduler::ProcessEventsToIdle();
{
SfxItemSet aSet1(pView1->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
SfxItemSet aSet2(pView2->GetPool(), svl::Items<SID_UNDO, SID_UNDO>{});
diff --git a/svx/source/form/fmsrcimp.cxx b/svx/source/form/fmsrcimp.cxx
index eb05a3676639..1cdc2df30255 100644
--- a/svx/source/form/fmsrcimp.cxx
+++ b/svx/source/form/fmsrcimp.cxx
@@ -311,13 +311,7 @@ FmSearchEngine::SearchResult FmSearchEngine::SearchSpecial(bool _bSearchForNull,
bool bMovedAround(false);
do
{
- Application::Reschedule();
- Application::Reschedule();
- // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
- // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
- // or anything like that. So within each loop we create one user event and handle one user event (and no
- // paintings and these), so the office seems to be frozen while searching.
- // FS - 70226 - 02.12.99
+ Application::Reschedule( true );
// the content to be compared currently
iterFieldLoop->xContents->getString(); // needed for wasNull
@@ -376,13 +370,7 @@ FmSearchEngine::SearchResult FmSearchEngine::SearchWildcard(const OUString& strE
bool bMovedAround(false);
do
{
- Application::Reschedule();
- Application::Reschedule();
- // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
- // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
- // or anything like that. So within each loop we create one user event and handle one user event (and no
- // paintings and these), so the office seems to be frozen while searching.
- // FS - 70226 - 02.12.99
+ Application::Reschedule( true );
// the content to be compared currently
OUString sCurrentCheck;
@@ -476,13 +464,7 @@ FmSearchEngine::SearchResult FmSearchEngine::SearchRegularApprox(const OUString&
bool bMovedAround(false);
do
{
- Application::Reschedule();
- Application::Reschedule();
- // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
- // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
- // or anything like that. So within each loop we create one user event and handle one user event (and no
- // paintings and these), so the office seems to be frozen while searching.
- // FS - 70226 - 02.12.99
+ Application::Reschedule( true );
// the content to be compared currently
OUString sCurrentCheck;
diff --git a/sw/source/ui/dbui/addresslistdialog.cxx b/sw/source/ui/dbui/addresslistdialog.cxx
index 96c77cd9edd7..3b88cac4dc0e 100644
--- a/sw/source/ui/dbui/addresslistdialog.cxx
+++ b/sw/source/ui/dbui/addresslistdialog.cxx
@@ -487,8 +487,7 @@ IMPL_LINK(SwAddressListDialog, StaticListBoxSelectHdl_Impl, void*, p, void)
m_pListLB->SetEntryText(m_sConnecting, pSelect, ITEMID_TABLE - 1);
// allow painting of the new entry
m_pListLB->Window::Invalidate(InvalidateFlags::Update);
- for (int i = 0; i < 10; ++i)
- Application::Reschedule();
+ Application::Reschedule( true );
}
pUserData = static_cast<AddressUserData_Impl*>(pSelect->GetUserData());
diff --git a/sw/source/ui/dbui/mmresultdialogs.cxx b/sw/source/ui/dbui/mmresultdialogs.cxx
index 1c3f17b4b52a..628c244b17d4 100644
--- a/sw/source/ui/dbui/mmresultdialogs.cxx
+++ b/sw/source/ui/dbui/mmresultdialogs.cxx
@@ -719,8 +719,7 @@ IMPL_LINK(SwMMResultSaveDialog, SaveOutputHdl_Impl, Button*, pButton, void)
while(true)
{
//time for other slots is needed
- for(sal_Int16 r = 0; r < 10; ++r)
- Application::Reschedule();
+ Application::Reschedule( true );
bool bFailed = false;
try
{
@@ -1088,8 +1087,7 @@ IMPL_LINK(SwMMResultEmailDialog, SendDocumentsHdl_Impl, Button*, pButton, void)
//help to force painting the dialog
//TODO/CLEANUP
//predetermined breaking point
- for ( sal_Int16 i = 0; i < 25; i++)
- Application::Reschedule();
+ Application::Reschedule( true );
for(sal_uInt32 nDoc = nBegin; nDoc < nEnd; ++nDoc)
{
SwDocMergeInfo& rInfo = xConfigItem->GetDocumentMergeInfo(nDoc);
@@ -1231,8 +1229,7 @@ IMPL_LINK(SwMMResultEmailDialog, SendDocumentsHdl_Impl, Button*, pButton, void)
aDesc.sBCC = m_sBCC;
pDlg->AddDocument( aDesc );
//help to force painting the dialog
- for ( sal_Int16 i = 0; i < 25; i++)
- Application::Reschedule();
+ Application::Reschedule( true );
//stop creating of data when dialog has been closed
if(!pDlg->IsVisible())
{
diff --git a/sw/source/uibase/dbui/dbmgr.cxx b/sw/source/uibase/dbui/dbmgr.cxx
index a08a6a923cdc..a481985738ac 100644
--- a/sw/source/uibase/dbui/dbmgr.cxx
+++ b/sw/source/uibase/dbui/dbmgr.cxx
@@ -147,11 +147,6 @@ using namespace ::com::sun::star;
namespace {
-void rescheduleGui() {
- for( sal_uInt16 i = 0; i < 25; i++)
- Application::Reschedule();
-}
-
void lcl_emitEvent(SfxEventHintId nEventId, sal_Int32 nStrId, SfxObjectShell* pDocShell)
{
SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId,
@@ -1257,7 +1252,7 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
pProgressDlg->SetCancelHdl( LINK(this, SwDBManager, PrtCancelHdl) );
pProgressDlg->Show();
- rescheduleGui();
+ Application::Reschedule( true );
}
if( bCreateSingleFile && !pTargetView )
@@ -1400,7 +1395,7 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
pProgressDlg->Update();
}
- rescheduleGui();
+ Application::Reschedule( true );
// Create a copy of the source document and work with that one instead of the source.
// If we're not in the single file mode (which requires modifying the document for the merging),
@@ -1570,7 +1565,7 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
}
else if( IsMergeOk() ) // && bCreateSingleFile
{
- rescheduleGui();
+ Application::Reschedule( true );
// sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
// unique fly names, do it here once.
@@ -1589,7 +1584,7 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
aLayout->AllCheckPageDescs();
}
- rescheduleGui();
+ Application::Reschedule( true );
if( IsMergeOk() && bMT_FILE )
{
@@ -1627,7 +1622,7 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
else if( xTargetDocShell.is() )
xTargetDocShell->DoClose();
- rescheduleGui();
+ Application::Reschedule( true );
pProgressDlg.disposeAndClear();
diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index fd5301a3b903..030dd0f3b438 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -107,11 +107,29 @@ thread redirects using Qt::BlockingQueuedConnection.
== General: non-main thread yield ==
Yielding from a non-main thread must not wait in the main thread, as this
-may block the main thread until some events happen.
+may block the main thread until some event happens.
Currently we wait on an extra conditional, which is cleared by the main event
loop.
+== General: processing all current events for DoYield ==
+
+This is easily implemented on all non-priority queue based implementations.
+Windows and MacOS both have a timestamp attached to their events / messages,
+so simply get the current time and just process anything < timestamp.
+For the KDE backend this is already the default behaviour - single event
+processing isn't even supported. The headless backend accomplishes this by
+just processing the a copy of the list of current events.
+
+Problematic in this regard is the Gtk+ backend. g_main_context_iteration
+dispatches "only those highest priority event sources". There is no real way
+to tell, when these became ready. I've added a workaround idea to the TODO
+list. FWIW: Qt runs just a single timer source in the glib mmain context,
+basically the same we're doing with the LO scheduler as a system event.
+
+The gen X11 backend has some levels of redirection, but needs uite some work
+to get this fixed.
+
== MacOS implementation details ==
Generally the Scheduler is handled as expected, except on resize, which is
@@ -224,3 +242,39 @@ workaround using a validation timestamp is better then the current peek,
remove, re-postEvent, which has to run in the main thread.
Originally I didn't evaluate, if the event is actually lost or just delayed.
+
+== Drop nMaxEvents from Gtk+ based backends ==
+
+gint last_priority = G_MAXINT;
+bool bWasEvent = false;
+do {
+ gint max_priority;
+ g_main_context_acquire( NULL );
+ bool bHasPending = g_main_context_prepare( NULL, &max_priority );
+ g_main_context_release( NULL );
+ if ( bHasPending )
+ {
+ if ( last_priority > max_priority )
+ {
+ bHasPending = g_main_context_iteration( NULL, bWait );
+ bWasEvent = bWasEvent || bHasPending;
+ }
+ else
+ bHasPending = false;
+ }
+}
+while ( bHasPending )
+
+The idea is to use g_main_context_prepare and keep the max_priority as an
+indicator. We cannot prevent running newer lower events, but we can prevent
+running new higher events, which should be sufficient for most stuff.
+
+This also touches user event processing, which currently runs as a high
+priority idle in the event loop.
+
+== Drop nMaxEvents from gen (X11) backend ==
+
+A few layers of indirection make this code hard to follow. The SalXLib::Yield
+and SalX11Display::Yield architecture makes it impossible to process just the
+current events. This really needs a refactorung and rearchitecture step, which
+will also affect the Gtk+ and KDE4 backend for the user event handling.
diff --git a/vcl/headless/svpinst.cxx b/vcl/headless/svpinst.cxx
index 1966727ac936..8a805ea345b1 100644
--- a/vcl/headless/svpinst.cxx
+++ b/vcl/headless/svpinst.cxx
@@ -180,7 +180,7 @@ bool SvpSalInstance::PostedEventsInQueue()
bool result = false;
{
osl::MutexGuard g(m_aEventGuard);
- result = m_aUserEvents.size() > 0;
+ result = !m_aUserEvents.empty();
}
return result;
}
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index 32efb0b31883..b663227cf244 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -597,6 +597,7 @@ bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
// handle available events
NSEvent* pEvent = nil;
+ NSDate *now = [[NSDate alloc] init];
do
{
SolarMutexReleaser aReleaser;
@@ -605,7 +606,7 @@ SAL_WNODEPRECATED_DECLARATIONS_PUSH
// 'NSAnyEventMask' is deprecated: first deprecated in macOS 10.12
pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask
SAL_WNODEPRECATED_DECLARATIONS_POP
- untilDate: nil
+ untilDate: now
inMode: NSDefaultRunLoopMode
dequeue: YES];
if( pEvent )
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index fdfc02a684d4..7f63ce0e4232 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -488,7 +488,7 @@ bool Application::Reschedule( bool i_bAllEvents )
void Scheduler::ProcessEventsToSignal(bool& bSignal)
{
- while (!bSignal && Application::Reschedule( false ) );
+ while (!bSignal && Application::Reschedule() );
}
void Scheduler::ProcessEventsToIdle()
@@ -516,7 +516,8 @@ void Scheduler::ProcessEventsToIdle()
Idle *pIdle = dynamic_cast<Idle*>( pSchedulerData->mpTask );
if ( pIdle && pIdle->IsActive() )
{
- SAL_WARN( "vcl.schedule", "Unprocessed Idle: " << pIdle->GetDebugName() );
+ SAL_WARN( "vcl.schedule", "Unprocessed Idle: "
+ << pIdle << " " << pIdle->GetDebugName() );
}
}
pSchedulerData = pSchedulerData->mpNext;
diff --git a/vcl/source/gdi/print2.cxx b/vcl/source/gdi/print2.cxx
index 2ccb80ce2a36..155d359d30f0 100644
--- a/vcl/source/gdi/print2.cxx
+++ b/vcl/source/gdi/print2.cxx
@@ -1232,8 +1232,7 @@ bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf,
pCurrAct->Execute( aPaintVDev.get() );
}
- if( !( nActionNum % 8 ) )
- Application::Reschedule();
+ Application::Reschedule( true );
}
const bool bOldMap = mbMap;
diff --git a/vcl/unx/kde4/KDEXLib.cxx b/vcl/unx/kde4/KDEXLib.cxx
index a44b9f1a3233..d56c67e008cb 100644
--- a/vcl/unx/kde4/KDEXLib.cxx
+++ b/vcl/unx/kde4/KDEXLib.cxx
@@ -293,8 +293,7 @@ bool KDEXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
// (it's ok to release it here, since even normal processYield() would
// temporarily do it while checking for new events)
SalYieldMutexReleaser aReleaser;
- Q_EMIT processYieldSignal( bWait, bHandleAllCurrentEvents );
- return false;
+ return Q_EMIT processYieldSignal( bWait, bHandleAllCurrentEvents );
}
}
diff --git a/vcl/unx/kde4/KDEXLib.hxx b/vcl/unx/kde4/KDEXLib.hxx
index 01076286c429..2fe497d019fa 100644
--- a/vcl/unx/kde4/KDEXLib.hxx
+++ b/vcl/unx/kde4/KDEXLib.hxx
@@ -68,7 +68,7 @@ class KDEXLib : public QObject, public SalXLib
Q_SIGNALS:
void startTimeoutTimerSignal();
- void processYieldSignal( bool bWait, bool bHandleAllCurrentEvents );
+ bool processYieldSignal( bool bWait, bool bHandleAllCurrentEvents );
css::uno::Reference< css::ui::dialogs::XFilePicker2 >
createFilePickerSignal( const css::uno::Reference< css::uno::XComponentContext >& );
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index 2da5e6bca80d..d7e519bbe527 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -503,14 +503,19 @@ static void ImplSalDispatchMessage( MSG* pMsg )
ImplSalPostDispatchMsg( pMsg, lResult );
}
-static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
+static bool ImplSalYield( const bool bWait, const bool bHandleAllCurrentEvents )
{
+ static sal_uInt32 nLastTicks = 0;
MSG aMsg;
bool bWasMsg = false, bOneEvent = false;
ImplSVData *const pSVData = ImplGetSVData();
WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
- int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
+ sal_uInt32 nCurTicks = 0;
+ if ( bHandleAllCurrentEvents )
+ nCurTicks = GetTickCount();
+
+ bool bHadNewerEvent = false;
do
{
bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
@@ -519,30 +524,38 @@ static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
bWasMsg = true;
TranslateMessage( &aMsg );
ImplSalDispatchMessage( &aMsg );
+ if ( bHandleAllCurrentEvents
+ && !bHadNewerEvent && aMsg.time > nCurTicks
+ && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks) )
+ bHadNewerEvent = true;
+ bOneEvent = !bHadNewerEvent;
}
- else
- // busy loop to catch the 0ms timeout
- // We don't need to busy loop, if we wait anyway.
- // Even if we didn't process the event directly, report it.
- if ( pTimer && pTimer->WantBusyLoop() && !bWait )
- {
- SwitchToThread();
- nMaxEvents++;
- bOneEvent = true;
- bWasMsg = true;
- }
- } while( --nMaxEvents && bOneEvent );
+ // 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) )
+ break;
+ }
+ while( true );
+
+ if ( bHandleAllCurrentEvents )
+ nLastTicks = nCurTicks;
// Also check that we don't wait when application already has quit
if ( bWait && !bWasMsg && !pSVData->maAppData.mbAppQuit )
{
if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
{
- // Ignore the scheduler wakeup message
+ bWasMsg = true;
TranslateMessage( &aMsg );
ImplSalDispatchMessage( &aMsg );
}
}
+
return bWasMsg;
}
commit aab6e4f898a9f6bee4df1258ec8f603078c97199
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Tue Aug 29 09:40:01 2017 +0200
Don't wait-yield non-main threads in the main thread
This prevents blocking the main thread by a yielding non-main thread.
The current solution is to wait on a condition, which is set by the
main thread on wakeup.
Change-Id: I8d680bb51a36ce1e0d3d4713d47d8e2ef93d7297
diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index 30a5be309741..fd5301a3b903 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -104,6 +104,14 @@ normally wait for the SolarMutex.
Eventually this will move into the GenericSolarMutex. KDE / Qt also does main
thread redirects using Qt::BlockingQueuedConnection.
+== General: non-main thread yield ==
+
+Yielding from a non-main thread must not wait in the main thread, as this
+may block the main thread until some events happen.
+
+Currently we wait on an extra conditional, which is cleared by the main event
+loop.
+
== MacOS implementation details ==
Generally the Scheduler is handled as expected, except on resize, which is
diff --git a/vcl/inc/osx/salinst.h b/vcl/inc/osx/salinst.h
index 3e8f1099bbf8..207f6cce7b3c 100644
--- a/vcl/inc/osx/salinst.h
+++ b/vcl/inc/osx/salinst.h
@@ -72,6 +72,8 @@ class AquaSalInstance : public SalInstance
{}
};
+ bool RunInMainYield( bool bHandleAllCurrentEvents );
+
public:
SalYieldMutex* mpSalYieldMutex; // Sal-Yield-Mutex
OUString maDefaultPrinter;
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index e438d069c530..32efb0b31883 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -536,6 +536,13 @@ void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
};
}
+bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents )
+{
+ OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents), boolean )
+ assert( false && "Don't call this from the main thread!" );
+ return false;
+}
+
bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
bool bHadEvent = false;
@@ -646,13 +653,17 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
}
maWaitingYieldCond.set();
}
- else if( bWait )
+ else
{
- // #i103162#
- // wait until the main thread has dispatched an event
- maWaitingYieldCond.reset();
- SolarMutexReleaser aReleaser;
- maWaitingYieldCond.wait();
+ bHadEvent = RunInMainYield( bHandleAllCurrentEvents );
+ if ( !bHadEvent && bWait )
+ {
+ // #i103162#
+ // wait until the main thread has dispatched an event
+ maWaitingYieldCond.reset();
+ SolarMutexReleaser aReleaser;
+ maWaitingYieldCond.wait();
+ }
}
// we get some apple events way too early
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index af2bc10e189a..2da5e6bca80d 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -558,31 +558,16 @@ bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
SolarMutexReleaser aReleaser;
if ( !IsMainThread() )
{
- if ( bWait )
+ // If you change the SendMessageW function, you might need to update
+ // the PeekMessage( ... PM_QS_POSTMESSAGE) calls!
+ bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
+ (WPARAM) false, (LPARAM) bHandleAllCurrentEvents );
+ if ( !bDidWork && bWait )
{
maWaitingYieldCond.reset();
maWaitingYieldCond.wait();
bDidWork = true;
}
- else {
- // #97739# A SendMessage call blocks until the called thread (here: the main thread)
- // returns. During a yield however, messages are processed in the main thread that might
- // result in a new message loop due to opening a dialog. Thus, SendMessage would not
- // return which will block this thread!
- // Solution: just give up the time slice and hope that messages are processed
- // by the main thread anyway (where all windows are created)
- // If the mainthread is not currently handling messages, then our SendMessage would
- // also do nothing, so this seems to be reasonable.
-
- // #i18883# only sleep if potential deadlock scenario, ie, when a dialog is open
- if( ImplGetSVData()->maAppData.mnModalMode )
- Sleep(1);
- else
- // If you change the SendMessageW function, you might need to update
- // the PeekMessage( ... PM_QS_POSTMESSAGE) calls!
- bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
- (WPARAM)bWait, (LPARAM)bHandleAllCurrentEvents );
- }
}
else
{
@@ -602,7 +587,8 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
switch ( nMsg )
{
case SAL_MSG_THREADYIELD:
- nRet = static_cast<LRESULT>(ImplSalYield( (bool)wParam, (bool)lParam ));
+ assert( !(bool)wParam );
+ nRet = static_cast<LRESULT>(ImplSalYield( false, (bool)lParam ));
rDef = FALSE;
break;
case SAL_MSG_STARTTIMER:
commit f585cf0d4552dc3eac7723ed0f9ecb50c005ec1f
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Mon Aug 28 19:58:32 2017 +0200
WIN run main thread redirects ignoring SolarMutex
This way we can drop all the special nReleased handling. Instead we use
the same mechanism as on Mac, where we keep the lock, but disable it for
the main thread. As a security measure we assert on duplicate redirects,
which should not happen.
As a result we can't use SendMessage on the main thread itself, which
would normally just call the WinProc directly. This could be accomplished
by converting the redirect bool into a counter, which should be safe, as
no other thread could acquire the SolarMutex, as we don't release it.
Change-Id: Icd87b3da37a2489f3cad2bc80215bf93fc41d388
diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx
index be6ea6f8a53c..d6e186350424 100644
--- a/include/vcl/svapp.hxx
+++ b/include/vcl/svapp.hxx
@@ -495,12 +495,6 @@ public:
*/
static void EndYield();
- /** Acquire SolarMutex after it has been temporarily dropped completely.
-
- This will Reschedule() on WNT and just acquire on other platforms.
- */
- static void ReAcquireSolarMutex(sal_uLong nReleased);
-
/** @brief Get the Solar Mutex for this thread.
Get the Solar Mutex that prevents other threads from accessing VCL
@@ -1491,7 +1485,7 @@ public:
~SolarMutexReleaser()
{
if ( mnReleased )
- Application::ReAcquireSolarMutex( mnReleased );
+ Application::AcquireSolarMutex( mnReleased );
}
};
diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index 0389dc94d723..30a5be309741 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -89,6 +89,21 @@ can be added to the scheduler reasonably.
= Implementation details =
+== General: main thread deferral ==
+
+Currently for Mac and Windows, we run main thread deferrals by disabling the
+SolarMutex using a boolean. In the case of the redirect, this makes
+tryToaAcquire and doAcquire return true or 1, while a release is ignored.
+Also the IsCurrentThread() mutex check function will act accordingly, so all
+the DBG_TESTSOLARMUTEX won't fail.
+
+Since we just disable the locks when we start running the deferred code in the
+main thread, we won't let the main thread run into stuff, where it would
+normally wait for the SolarMutex.
+
+Eventually this will move into the GenericSolarMutex. KDE / Qt also does main
+thread redirects using Qt::BlockingQueuedConnection.
+
== MacOS implementation details ==
Generally the Scheduler is handled as expected, except on resize, which is
@@ -100,9 +115,8 @@ Like the Windows backend, all Cocoa / GUI handling also has to be run in
the main thread. We're emulating Windows out-of-order PeekMessage processing,
via a YieldWakeupEvent and two conditionals. When in a RUNINMAIN call, all
the DBG_TESTSOLARMUTEX calls are disabled, as we can't release the SolarMutex,
-but we can prevent running any other SolarMutex based code. Same for all the
-SolarMutex acquire and release calls, so the calling and the main thread
-don't deadlock.
+but we can prevent running any other SolarMutex based code. For more info
+read the "General: main thread deferral" section.
We can neigher rely on MacOS dispatch_sync code block execution nor the
message handling, as both can't be priorized or filtered and the first
@@ -136,6 +150,10 @@ the timer callback message, which is checked before starting the Scheduler.
This way we can end with multiple timer callback message in the queue, which
we were asserting.
+To run the required GUI code in the main thread without unlocking the
+SolarMutex, we "disable" it. For more infos read the "General: main thread
+deferral" section.
+
== KDE implementation details ==
This implementation also works as intended. But there is a different Yield
diff --git a/vcl/headless/svpinst.cxx b/vcl/headless/svpinst.cxx
index b75a035466f1..1966727ac936 100644
--- a/vcl/headless/svpinst.cxx
+++ b/vcl/headless/svpinst.cxx
@@ -304,12 +304,9 @@ SalBitmap* SvpSalInstance::CreateSalBitmap()
#endif
}
-bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
+bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
- (void) nReleased;
- assert(nReleased == 0); // not implemented
// first, check for already queued events.
-
std::list< SalUserEvent > aEvents;
{
osl::MutexGuard g(m_aEventGuard);
diff --git a/vcl/inc/headless/svpinst.hxx b/vcl/inc/headless/svpinst.hxx
index e8bb96ab68ac..0883981c4406 100644
--- a/vcl/inc/headless/svpinst.hxx
+++ b/vcl/inc/headless/svpinst.hxx
@@ -155,7 +155,7 @@ public:
// wait next event and dispatch
// must returned by UserEvent (SalFrame::PostEvent)
// and timer
- virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
+ virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents) override;
virtual bool AnyInput( VclInputFlags nType ) override;
virtual bool IsMainThread() const override { return true; }
diff --git a/vcl/inc/osx/salinst.h b/vcl/inc/osx/salinst.h
index 7c47f71a3696..3e8f1099bbf8 100644
--- a/vcl/inc/osx/salinst.h
+++ b/vcl/inc/osx/salinst.h
@@ -115,8 +115,7 @@ public:
virtual comphelper::SolarMutex* GetYieldMutex() override;
virtual sal_uInt32 ReleaseYieldMutex( bool bUnlockAll = false ) override;
virtual void AcquireYieldMutex( sal_uInt32 nCount = 1 ) override;
- virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents,
- sal_uLong nReleased) override;
+ virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents) override;
virtual bool AnyInput( VclInputFlags nType ) override;
virtual SalMenu* CreateMenu( bool bMenuBar, Menu* pVCLMenu ) override;
virtual void DestroyMenu( SalMenu* ) override;
diff --git a/vcl/inc/salinst.hxx b/vcl/inc/salinst.hxx
index 12959cc44280..8418a1ed8126 100644
--- a/vcl/inc/salinst.hxx
+++ b/vcl/inc/salinst.hxx
@@ -133,7 +133,7 @@ public:
* If bHandleAllCurrentEvents - dispatch multiple posted
* user events. Returns true if events were processed.
*/
- virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) = 0;
+ virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents) = 0;
virtual bool AnyInput( VclInputFlags nType ) = 0;
// menus
diff --git a/vcl/inc/unx/gtk/gtkinst.hxx b/vcl/inc/unx/gtk/gtkinst.hxx
index 1585c778afac..595a7adb7089 100644
--- a/vcl/inc/unx/gtk/gtkinst.hxx
+++ b/vcl/inc/unx/gtk/gtkinst.hxx
@@ -208,7 +208,7 @@ public:
const SystemGraphicsData* = nullptr ) override;
virtual SalBitmap* CreateSalBitmap() override;
- virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
+ virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents) override;
virtual bool AnyInput( VclInputFlags nType ) override;
// impossible to handle correctly, as "main thread" depends on the dispatch mutex
virtual bool IsMainThread() const override { return false; }
diff --git a/vcl/inc/unx/salinst.h b/vcl/inc/unx/salinst.h
index 2307410fe69e..59464b4c60e5 100644
--- a/vcl/inc/unx/salinst.h
+++ b/vcl/inc/unx/salinst.h
@@ -74,7 +74,7 @@ public:
virtual SalSession* CreateSalSession() override;
virtual OpenGLContext* CreateOpenGLContext() override;
- virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
+ virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents) override;
virtual bool AnyInput( VclInputFlags nType ) override;
virtual bool IsMainThread() const override { return true; }
diff --git a/vcl/inc/win/salinst.h b/vcl/inc/win/salinst.h
index 6efecbd6bd5e..8772ee00fa22 100644
--- a/vcl/inc/win/salinst.h
+++ b/vcl/inc/win/salinst.h
@@ -34,6 +34,9 @@ public:
/// The Yield mutex ensures that only one thread calls into VCL
SalYieldMutex* mpSalYieldMutex;
+ osl::Condition maWaitingYieldCond;
+ bool mbNoYieldLock;
+
public:
WinSalInstance();
virtual ~WinSalInstance() override;
@@ -63,7 +66,7 @@ public:
virtual void AcquireYieldMutex( sal_uInt32 nCount = 1 ) override;
virtual bool IsMainThread() const override;
- virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
+ virtual bool DoYield(bool bWait, bool bHandleAllCurrentEvents) override;
virtual bool AnyInput( VclInputFlags nType ) override;
virtual SalMenu* CreateMenu( bool bMenuBar, Menu* ) override;
virtual void DestroyMenu( SalMenu* ) override;
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index ea2b3ed294b8..e438d069c530 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -536,10 +536,8 @@ void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
};
}
-bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
+bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
- (void) nReleased;
- assert(nReleased == 0); // not implemented
bool bHadEvent = false;
// ensure that the per thread autorelease pool is top level and
diff --git a/vcl/qa/cppunit/timer.cxx b/vcl/qa/cppunit/timer.cxx
index 28f4284f1430..7b712bd76c04 100644
--- a/vcl/qa/cppunit/timer.cxx
+++ b/vcl/qa/cppunit/timer.cxx
@@ -148,7 +148,7 @@ void TimerTest::testIdleMainloop()
// can't test this via Application::Yield since this
// also processes all tasks directly via the scheduler.
pSVData->maAppData.mnDispatchLevel++;
- pSVData->mpDefInst->DoYield(true, false, 0);
+ pSVData->mpDefInst->DoYield(true, false);
pSVData->maAppData.mnDispatchLevel--;
}
CPPUNIT_ASSERT_MESSAGE("mainloop idle triggered", bTriggered);
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index 5959b3f5b1cb..fdfc02a684d4 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -451,12 +451,12 @@ void Application::Execute()
pSVData->maAppData.mbInAppExecute = false;
}
-inline bool ImplYield(bool i_bWait, bool i_bAllEvents, sal_uLong const nReleased)
+inline bool ImplYield(bool i_bWait, bool i_bAllEvents)
{
ImplSVData* pSVData = ImplGetSVData();
SAL_INFO("vcl.schedule", "Enter ImplYield: " << (i_bWait ? "wait" : "no wait") <<
- ": " << (i_bAllEvents ? "all events" : "one event") << ": " << nReleased);
+ ": " << (i_bAllEvents ? "all events" : "one event"));
// TODO: there's a data race here on WNT only because ImplYield may be
// called without SolarMutex; if we can get rid of LazyDelete (with VclPtr)
@@ -466,10 +466,8 @@ inline bool ImplYield(bool i_bWait, bool i_bAllEvents, sal_uLong const nReleased
// do not wait for events if application was already quit; in that
// case only dispatch events already available
- bool bProcessedEvent =
- pSVData->mpDefInst->DoYield(
- i_bWait && !pSVData->maAppData.mbAppQuit,
- i_bAllEvents, nReleased);
+ bool bProcessedEvent = pSVData->mpDefInst->DoYield(
+ i_bWait && !pSVData->maAppData.mbAppQuit, i_bAllEvents );
pSVData->maAppData.mnDispatchLevel--;
@@ -485,7 +483,7 @@ inline bool ImplYield(bool i_bWait, bool i_bAllEvents, sal_uLong const nReleased
bool Application::Reschedule( bool i_bAllEvents )
{
- return ImplYield(false, i_bAllEvents, 0);
+ return ImplYield(false, i_bAllEvents);
}
void Scheduler::ProcessEventsToSignal(bool& bSignal)
@@ -537,27 +535,7 @@ SAL_DLLPUBLIC_EXPORT void unit_lok_process_events_to_idle()
void Application::Yield()
{
- ImplYield(true, false, 0);
-}
-
-void Application::ReAcquireSolarMutex(sal_uLong const nReleased)
-{
- // 0 would mean that events/timers will be handled without locking
- // SolarMutex (racy)
- SAL_WARN_IF(nReleased == 0, "vcl", "SolarMutexReleaser without SolarMutex");
-#ifdef _WIN32
- if (nReleased == 0 || ImplGetSVData()->mbDeInit) //do not Yield in DeInitVCL
- AcquireSolarMutex(nReleased);
- else
- ImplYield(false, false, nReleased);
-#else
- // a) Yield is not needed on non-WNT platforms
- // b) some Yield implementations for X11 (e.g. kde4) make it non-obvious
- // how to use nReleased
- // c) would require a review of what all Yield implementations do
- // currently _before_ releasing SolarMutex that would run without lock
- AcquireSolarMutex(nReleased);
-#endif
+ ImplYield(true, false);
}
IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplQuitMsg, void*, void )
diff --git a/vcl/unx/generic/app/salinst.cxx b/vcl/unx/generic/app/salinst.cxx
index 456c238a1d08..897d955ddccb 100644
--- a/vcl/unx/generic/app/salinst.cxx
+++ b/vcl/unx/generic/app/salinst.cxx
@@ -166,10 +166,8 @@ bool X11SalInstance::AnyInput(VclInputFlags nType)
return bRet;
}
-bool X11SalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
+bool X11SalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
- (void) nReleased;
- assert(nReleased == 0); // not implemented
return mpXLib->Yield( bWait, bHandleAllCurrentEvents );
}
diff --git a/vcl/unx/gtk/gtkinst.cxx b/vcl/unx/gtk/gtkinst.cxx
index 080b2a9c0345..1caee7f637aa 100644
--- a/vcl/unx/gtk/gtkinst.cxx
+++ b/vcl/unx/gtk/gtkinst.cxx
@@ -409,10 +409,8 @@ void GtkInstance::RemoveTimer ()
m_pTimer = nullptr;
}
-bool GtkInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
+bool GtkInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
- (void) nReleased;
- assert(nReleased == 0); // not implemented
EnsureInit();
return GetGtkSalData()->Yield( bWait, bHandleAllCurrentEvents );
}
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index b750ca5f4421..af2bc10e189a 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -111,6 +111,7 @@ public:
explicit SalYieldMutex();
virtual bool IsCurrentThread() const override;
+ virtual bool tryToAcquire() override;
};
SalYieldMutex::SalYieldMutex()
@@ -138,6 +139,8 @@ void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
WinSalInstance* pInst = GetSalData()->mpFirstInstance;
if ( pInst && pInst->IsMainThread() )
{
+ if ( pInst->mbNoYieldLock )
+ return;
// tdf#96887 If this is the main thread, then we must wait for two things:
// - the mpSalYieldMutex being freed
// - SendMessage() being triggered
@@ -166,15 +169,31 @@ void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
{
- sal_uInt32 nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll );
+ WinSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if ( pInst && pInst->mbNoYieldLock && pInst->IsMainThread() )
+ return 1;
+ sal_uInt32 nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll );
// wake up ImplSalYieldMutexAcquireWithWait() after release
if ( 0 == m_nCount )
m_condition.set();
-
return nCount;
}
+bool SalYieldMutex::tryToAcquire()
+{
+ WinSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if ( pInst )
+ {
+ if ( pInst->mbNoYieldLock && pInst->IsMainThread() )
+ return true;
+ else
+ return comphelper::GenericSolarMutex::tryToAcquire();
+ }
+ else
+ return false;
+}
+
void ImplSalYieldMutexAcquireWithWait( sal_uLong nCount )
{
WinSalInstance* pInst = GetSalData()->mpFirstInstance;
@@ -185,10 +204,7 @@ void ImplSalYieldMutexAcquireWithWait( sal_uLong nCount )
bool ImplSalYieldMutexTryToAcquire()
{
WinSalInstance* pInst = GetSalData()->mpFirstInstance;
- if ( pInst )
- return pInst->mpSalYieldMutex->tryToAcquire();
- else
- return false;
+ return pInst ? pInst->mpSalYieldMutex->tryToAcquire() : false;
}
void ImplSalYieldMutexRelease()
@@ -203,8 +219,11 @@ void ImplSalYieldMutexRelease()
bool SalYieldMutex::IsCurrentThread() const
{
- // For the Windows backend, the LO identifier is the system thread ID
- return m_nThreadId == GetCurrentThreadId();
+ if ( !GetSalData()->mpFirstInstance->mbNoYieldLock )
+ // For the Windows backend, the LO identifier is the system thread ID
+ return m_nThreadId == GetCurrentThreadId();
+ else
+ return GetSalData()->mpFirstInstance->IsMainThread();
}
void SalData::initKeyCodeMap()
@@ -442,9 +461,10 @@ void DestroySalInstance( SalInstance* pInst )
}
WinSalInstance::WinSalInstance()
+ : mhComWnd( nullptr )
+ , mbNoYieldLock( false )
{
- mhComWnd = nullptr;
- mpSalYieldMutex = new SalYieldMutex();
+ mpSalYieldMutex = new SalYieldMutex();
mpSalYieldMutex->acquire();
}
@@ -483,8 +503,7 @@ static void ImplSalDispatchMessage( MSG* pMsg )
ImplSalPostDispatchMsg( pMsg, lResult );
}
-bool
-ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
+static bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
{
MSG aMsg;
bool bWasMsg = false, bOneEvent = false;
@@ -533,38 +552,44 @@ bool WinSalInstance::IsMainThread() const
return pSalData->mnAppThreadId == GetCurrentThreadId();
}
-bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
+bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
bool bDidWork = false;
- // NOTE: if nReleased != 0 this will be called without SolarMutex
- // so don't do anything dangerous before releasing it here
- sal_uInt32 const nCount = (nReleased != 0)
- ? nReleased : mpSalYieldMutex->release( true );
+ SolarMutexReleaser aReleaser;
if ( !IsMainThread() )
{
- // #97739# A SendMessage call blocks until the called thread (here: the main thread)
- // returns. During a yield however, messages are processed in the main thread that might
- // result in a new message loop due to opening a dialog. Thus, SendMessage would not
- // return which will block this thread!
- // Solution: just give up the time slice and hope that messages are processed
- // by the main thread anyway (where all windows are created)
- // If the mainthread is not currently handling messages, then our SendMessage would
- // also do nothing, so this seems to be reasonable.
-
- // #i18883# only sleep if potential deadlock scenario, ie, when a dialog is open
- if( ImplGetSVData()->maAppData.mnModalMode )
- Sleep(1);
- else
- // If you change the SendMessageW function, you might need to update
- // the PeekMessage( ... PM_QS_POSTMESSAGE) calls!
- bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD, (WPARAM)bWait, (LPARAM)bHandleAllCurrentEvents );
+ if ( bWait )
+ {
+ maWaitingYieldCond.reset();
+ maWaitingYieldCond.wait();
+ bDidWork = true;
+ }
+ else {
+ // #97739# A SendMessage call blocks until the called thread (here: the main thread)
+ // returns. During a yield however, messages are processed in the main thread that might
+ // result in a new message loop due to opening a dialog. Thus, SendMessage would not
+ // return which will block this thread!
+ // Solution: just give up the time slice and hope that messages are processed
+ // by the main thread anyway (where all windows are created)
+ // If the mainthread is not currently handling messages, then our SendMessage would
+ // also do nothing, so this seems to be reasonable.
+
+ // #i18883# only sleep if potential deadlock scenario, ie, when a dialog is open
+ if( ImplGetSVData()->maAppData.mnModalMode )
+ Sleep(1);
+ else
+ // If you change the SendMessageW function, you might need to update
+ // the PeekMessage( ... PM_QS_POSTMESSAGE) calls!
+ bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
+ (WPARAM)bWait, (LPARAM)bHandleAllCurrentEvents );
+ }
}
else
{
- if (nReleased == 0) // tdf#99383 ReAcquireSolarMutex shouldn't Yield
- bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
+ bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
+ if ( bDidWork )
+ maWaitingYieldCond.set();
}
- mpSalYieldMutex->acquire( nCount );
return bDidWork;
}
@@ -572,6 +597,7 @@ bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong
LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, int& rDef )
{
LRESULT nRet = 0;
+ WinSalInstance *pInst = GetSalData()->mpFirstInstance;
switch ( nMsg )
{
@@ -594,19 +620,31 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
static_cast<WinSalTimer*>(ImplGetSVData()->maSchedCtx.mpSalTimer)->ImplStop();
break;
case SAL_MSG_CREATEFRAME:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
nRet = reinterpret_cast<LRESULT>(ImplSalCreateFrame( GetSalData()->mpFirstInstance, reinterpret_cast<HWND>(lParam), (SalFrameStyleFlags)wParam ));
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_RECREATEHWND:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
nRet = reinterpret_cast<LRESULT>(ImplSalReCreateHWND( reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false ));
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_RECREATECHILDHWND:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
nRet = reinterpret_cast<LRESULT>(ImplSalReCreateHWND( reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true ));
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_DESTROYFRAME:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
delete reinterpret_cast<SalFrame*>(lParam);
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_DESTROYHWND:
@@ -622,19 +660,31 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
rDef = FALSE;
break;
case SAL_MSG_CREATEOBJECT:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
nRet = reinterpret_cast<LRESULT>(ImplSalCreateObject( GetSalData()->mpFirstInstance, reinterpret_cast<WinSalFrame*>(lParam) ));
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_DESTROYOBJECT:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
delete reinterpret_cast<SalObject*>(lParam);
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_GETDC:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
nRet = reinterpret_cast<LRESULT>(GetDCEx( reinterpret_cast<HWND>(wParam), nullptr, DCX_CACHE ));
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_RELEASEDC:
+ assert( !pInst->mbNoYieldLock );
+ pInst->mbNoYieldLock = true;
ReleaseDC( reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam) );
+ pInst->mbNoYieldLock = false;
rDef = FALSE;
break;
case SAL_MSG_TIMER_CALLBACK:
@@ -644,7 +694,7 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
MSG aMsg;
bool bValidMSG = pTimer->IsValidWPARAM( wParam );
// PM_QS_POSTMESSAGE is needed, so we don't process the SendMessage from DoYield!
- while ( PeekMessageW(&aMsg, GetSalData()->mpFirstInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK,
+ 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" );
diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx
index 4bcb1d6e09da..7195b3b91fb0 100644
--- a/vcl/win/window/salframe.cxx
+++ b/vcl/win/window/salframe.cxx
@@ -1051,10 +1051,15 @@ void WinSalFrame::ReleaseGraphics( SalGraphics* pGraphics )
if ( mpGraphics2->getDefPal() )
SelectPalette( mpGraphics2->getHDC(), mpGraphics2->getDefPal(), TRUE );
mpGraphics2->DeInitGraphics();
- SendMessageW( pSalData->mpFirstInstance->mhComWnd,
- SAL_MSG_RELEASEDC,
- reinterpret_cast<WPARAM>(mhWnd),
- reinterpret_cast<LPARAM>(mpGraphics2->getHDC()) );
+ // we don't want to run the WinProc in the main thread directly
+ // so we don't hit the mbNoYieldLock assert
+ if ( !pSalData->mpFirstInstance->IsMainThread() )
+ SendMessageW( pSalData->mpFirstInstance->mhComWnd,
+ SAL_MSG_RELEASEDC,
+ reinterpret_cast<WPARAM>(mhWnd),
+ reinterpret_cast<LPARAM>(mpGraphics2->getHDC()) );
+ else
+ ReleaseDC( mhWnd, mpGraphics2->getHDC() );
mpGraphics2->setHDC(nullptr);
pSalData->mnCacheDCInUse--;
}
commit 01e4d52731979154cd34b880e235b237ad3b0dee
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Fri Aug 25 22:03:47 2017 +0200
OSX fix ODK example builds with enabled SIP
The "System Integrity Protection”, introduced in macOS El Capitan,
strips DYLD_* environment variables from all calls of software in
/bin and /usr/bin.
As a workaround we copy the shell to a temporary file and use it
in our "sub-make" calls to build the examples.
Change-Id: I3f07492782d56e153e8fcdea605a042ec1898276
diff --git a/odk/CustomTarget_build-examples.mk b/odk/CustomTarget_build-examples.mk
index 81ac3a1437c6..f7dc1db1e30d 100644
--- a/odk/CustomTarget_build-examples.mk
+++ b/odk/CustomTarget_build-examples.mk
@@ -93,6 +93,11 @@ ifneq ($(gb_SUPPRESS_TESTS),)
@true
else
$(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),CHK,1)
+ifeq (MACOSX,$(OS))
+ $(eval ODK_BUILD_SHELL := $(shell $(gb_MKTEMP)))
+ cp /bin/sh "$(ODK_BUILD_SHELL)"
+ chmod 0700 "$(ODK_BUILD_SHELL)"
+endif
(saved_library_path=$${$(gb_Helper_LIBRARY_PATH_VAR)} && . $< \
$(if $(filter MACOSX,$(OS)),, \
&& $(gb_Helper_LIBRARY_PATH_VAR)=$$saved_library_path) \
@@ -100,11 +105,16 @@ else
UserInstallation=$(call gb_Helper_make_url,$(call gb_CustomTarget_get_workdir,odk/build-examples)/user) \
$(foreach my_dir,$(my_example_dirs), \
&& (cd $(INSTDIR)/$(SDKDIRNAME)/examples/$(my_dir) \
- && printf 'yes\n' | LC_ALL=C make))) \
+ && printf 'yes\n' | LC_ALL=C make \
+ $(if $(filter MACOSX,$(OS)), SHELL=$(ODK_BUILD_SHELL), )))) \
>$(call gb_CustomTarget_get_workdir,odk/build-examples)/log 2>&1 \
|| (RET=$$? \
+ $(if $(filter MACOSX,$(OS)), && rm -f $(ODK_BUILD_SHELL) , ) \
&& cat $(call gb_CustomTarget_get_workdir,odk/build-examples)/log \
&& exit $$RET)
+ifeq (MACOSX,$(OS))
+ -rm -f $(ODK_BUILD_SHELL)
+endif
endif
$(call gb_CustomTarget_get_workdir,odk/build-examples)/setsdkenv: \
commit e3f7790785a9f208e02dbde2b0fafc2759f78a2b
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Fri Aug 25 14:52:38 2017 +0200
Disable ScHeaderFooterTextCursor scenario test
Just like the ScHeaderFooterTextObj, it depends on weak referenced
objects, which might be gone / cleaned up at test time.
Change-Id: I52503646c51b0915df11f5c7f90c16208e45879a
diff --git a/sc/qa/unoapi/sc_4.sce b/sc/qa/unoapi/sc_4.sce
index d631ac99878b..ce879cc83272 100644
--- a/sc/qa/unoapi/sc_4.sce
+++ b/sc/qa/unoapi/sc_4.sce
@@ -36,6 +36,8 @@
# The css::text::XTextRange test fails often when the weak SHF_ContentObj is
# already gone. If just this test is disabled, later tests of this object fail
# too, so this disables the whole interface.
+# Same for ScHeaderFooterTextCursor.
+# -o sc.ScHeaderFooterTextCursor
# -o sc.ScHeaderFooterTextObj
-o sc.ScIndexEnumeration_CellAnnotationsEnumeration
-o sc.ScIndexEnumeration_CellAreaLinksEnumeration
commit f6d220516d8d3b7525153d21db3f8f9c356d69d3
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Fri Aug 25 11:12:24 2017 +0200
Cleanup dbaccess module makefile
Change-Id: Ic379a8362bb25fb1635c9900bbf247c8f2943c74
diff --git a/dbaccess/Module_dbaccess.mk b/dbaccess/Module_dbaccess.mk
index d9f80fc11540..4d1b9f44d623 100644
--- a/dbaccess/Module_dbaccess.mk
+++ b/dbaccess/Module_dbaccess.mk
@@ -32,36 +32,29 @@ $(eval $(call gb_Module_add_l10n_targets,dbaccess,\
))
ifneq ($(OS),IOS)
-ifeq ($(ENABLE_FIREBIRD_SDBC),TRUE)
-$(eval $(call gb_Module_add_check_targets,dbaccess,\
- CppunitTest_dbaccess_firebird_test \
-))
-endif
$(eval $(call gb_Module_add_check_targets,dbaccess,\
CppunitTest_dbaccess_dialog_save \
CppunitTest_dbaccess_empty_stdlib_save \
CppunitTest_dbaccess_nolib_save \
CppunitTest_dbaccess_macros_test \
- $(if $(ENABLE_JAVA), \
- CppunitTest_dbaccess_RowSetClones) \
))
ifeq ($(ENABLE_JAVA),TRUE)
+
$(eval $(call gb_Module_add_check_targets,dbaccess,\
+ CppunitTest_dbaccess_RowSetClones \
CppunitTest_dbaccess_hsqldb_test \
))
-endif
# This runs a suite of performance tests on embedded firebird and HSQLDB.
# Instructions on running the test can be found in qa/unit/embeddedb_performancetest
ifeq ($(ENABLE_FIREBIRD_SDBC),TRUE)
-ifeq ($(ENABLE_JAVA),TRUE)
$(eval $(call gb_Module_add_check_targets,dbaccess,\
+ CppunitTest_dbaccess_firebird_test \
CppunitTest_dbaccess_embeddeddb_performancetest \
))
endif
-endif
$(eval $(call gb_Module_add_subsequentcheck_targets,dbaccess,\
JunitTest_dbaccess_complex \
@@ -69,19 +62,20 @@ $(eval $(call gb_Module_add_subsequentcheck_targets,dbaccess,\
))
ifneq ($(DISABLE_PYTHON),TRUE)
-ifneq ($(ENABLE_JAVA),)
$(eval $(call gb_Module_add_subsequentcheck_targets,dbaccess,\
PythonTest_dbaccess_python \
))
endif
-endif
+
+endif # ifeq ($(ENABLE_JAVA),TRUE)
# screenshots
$(eval $(call gb_Module_add_screenshot_targets,dbaccess,\
CppunitTest_dbaccess_dialogs_test \
))
-endif
+endif # ifneq ($(OS),IOS)
+
endif
# vim: set noet sw=4 ts=4:
commit 80517aa00583544329982467ee2ae2d4685df57f
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Thu Aug 24 13:41:37 2017 +0200
tdf#111994 WIN workaround PostMessage delays
Fixes the "Multiple timers in queue" assertion by effectively
removing it.
When debugging it became obvious, that PostMessage returns, even
if the message was not yet added to the message queue.
The assert happens, because we start the timer in the Scheduler
before Invoke(), so it fires, if we block in Invoke(), and then
reset the timer after Invoke, if there were changes to the Task
list.
In this case it fires during Invoke(), the message is added. We
restart the timer, first by stopping it (we wait in
DeleteTimerQueueTimer, to be sure the timer function has either
finished or was not run). And the try to remove the message with
PeekMessageW, which doesn't remove the posted message.
Then the timer is restarted, and when the event is processed, we
end up with an additional timer event, which was asserted.
As a fix this adds a (microsecond) timestamp to the timer message,
which is validated in the WinProc function. So if we stop the
timer too fast, the event is ignored based on the timestamp.
And while at it, the patch moves timer related variables from
SalData into WinSalTimer.
Change-Id: Ib840a421e8bd040d40f39473e1d44491e5b332bd
diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index 0cd5a24d5f83..0389dc94d723 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -129,6 +129,13 @@ Therefore the current solution always starts a (threaded) timer even for the
instant Idles and syncs to this timer message in the main dispatch loop.
Using SwitchToThread(), this seem to work reasonably well.
+An additional workaround is implemented for the delayed queuing of posted
+messages, where PeekMessage in WinSalTimer::Stop() won't be able remove the
+just posted timer callback message. We handle this by adding a timestamp to
+the timer callback message, which is checked before starting the Scheduler.
+This way we can end with multiple timer callback message in the queue, which
+we were asserting.
+
== KDE implementation details ==
This implementation also works as intended. But there is a different Yield
@@ -183,3 +190,11 @@ mbStatic workaround from the Task class.
This would probably get rid of most of the MacOS and Windows implementation
details / workarounds, but is quite probably a large amount of work.
+
+== Re-evaluate the MacOS ImplNSAppPostEvent ==
+
+Probably a solution comparable to the Windows backends delayed PostMessage
+workaround using a validation timestamp is better then the current peek,
+remove, re-postEvent, which has to run in the main thread.
+
+Originally I didn't evaluate, if the event is actually lost or just delayed.
diff --git a/vcl/inc/win/saldata.hxx b/vcl/inc/win/saldata.hxx
index bc5b9c5db1eb..245d986915b1 100644
--- a/vcl/inc/win/saldata.hxx
+++ b/vcl/inc/win/saldata.hxx
@@ -84,8 +84,6 @@ public:
long* mpDitherDiff; // Dither mapping table
BYTE* mpDitherLow; // Dither mapping table
BYTE* mpDitherHigh; // Dither mapping table
- HANDLE mnTimerId; ///< Windows timer id
- bool mbOnIdleRunScheduler; ///< Run yield until the scheduler processed the idle
HHOOK mhSalObjMsgHook; // hook to get interesting msg for SalObject
HWND mhWantLeaveMsg; // window handle, that want a MOUSELEAVE message
AutoTimer* mpMouseLeaveTimer; // Timer for MouseLeave Test
@@ -178,8 +176,6 @@ void ImplSalYieldMutexRelease();
LRESULT CALLBACK SalFrameWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
-void EmitTimerCallback();
-
void SalTestMouseLeave();
bool ImplHandleSalObjKeyMsg( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
diff --git a/vcl/inc/win/saltimer.h b/vcl/inc/win/saltimer.h
index 084a25745b87..9107dd1a0b19 100644
--- a/vcl/inc/win/saltimer.h
+++ b/vcl/inc/win/saltimer.h
@@ -24,16 +24,39 @@
class WinSalTimer : public SalTimer
{
+ HANDLE m_nTimerId; ///< Windows timer id
+ sal_uInt32 m_nTimerStartTicks; ///< system ticks at timer start % SAL_MAX_UINT32
+ bool m_bPollForMessage; ///< Run yield until a message is caught (most likely the 0ms timer)
+
public:
- WinSalTimer() {}
+ WinSalTimer();
virtual ~WinSalTimer() override;
virtual void Start(sal_uIntPtr nMS) override;
virtual void Stop() override;
+
+ inline bool IsValidWPARAM( WPARAM wParam ) const;
+
+ inline bool PollForMessage() const;
+
+ // The Impl functions are just public to be called from the static
+ // SalComWndProc on main thread redirect! Otherwise they would be private.
+ // They must be called from the main application thread only!
+
+ void ImplStart( sal_uIntPtr nMS );
+ void ImplStop();
+ void ImplEmitTimerCallback();
};
-void ImplSalStartTimer( sal_uIntPtr nMS );
-void ImplSalStopTimer();
+inline bool WinSalTimer::IsValidWPARAM( WPARAM aWPARAM ) const
+{
+ return aWPARAM == m_nTimerStartTicks;
+}
+
+inline bool WinSalTimer::PollForMessage() const
+{
+ return m_bPollForMessage;
+}
#endif
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index 12f95aac31f2..b750ca5f4421 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -247,8 +247,6 @@ SalData::SalData()
mpDitherDiff = nullptr; // Dither mapping table
mpDitherLow = nullptr; // Dither mapping table
mpDitherHigh = nullptr; // Dither mapping table
- mnTimerId = nullptr; // windows timer id
- mbOnIdleRunScheduler = false; // if yield is idle, run the scheduler
mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
@@ -490,7 +488,8 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
{
MSG aMsg;
bool bWasMsg = false, bOneEvent = false;
- SalData *const pSalData = GetSalData();
+ ImplSVData *const pSVData = ImplGetSVData();
+ WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1;
do
@@ -506,7 +505,7 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
// busy loop to catch the 0ms timeout
// We don't need to busy loop, if we wait anyway.
// Even if we didn't process the event directly, report it.
- if ( pSalData->mbOnIdleRunScheduler && !bWait )
+ if ( pTimer && pTimer->WantBusyLoop() && !bWait )
{
SwitchToThread();
nMaxEvents++;
@@ -516,7 +515,7 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
} while( --nMaxEvents && bOneEvent );
// Also check that we don't wait when application already has quit
- if ( bWait && !bWasMsg && !ImplGetSVData()->maAppData.mbAppQuit )
+ if ( bWait && !bWasMsg && !pSVData->maAppData.mbAppQuit )
{
if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
{
@@ -582,17 +581,17 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
break;
case SAL_MSG_STARTTIMER:
{
- sal_uLong nTime = GetTickCount();
- if ( nTime < (sal_uLong) lParam )
- nTime = (sal_uLong) lParam - nTime;
+ sal_uInt64 nTime = tools::Time::GetSystemTicks();
+ if ( nTime < (sal_uInt64) lParam )
+ nTime = (sal_uInt64) lParam - nTime;
else
nTime = 0;
- ImplSalStartTimer( nTime );
+ static_cast<WinSalTimer*>(ImplGetSVData()->maSchedCtx.mpSalTimer)->ImplStart( nTime );
rDef = FALSE;
break;
}
case SAL_MSG_STOPTIMER:
- ImplSalStopTimer();
+ static_cast<WinSalTimer*>(ImplGetSVData()->maSchedCtx.mpSalTimer)->ImplStop();
break;
case SAL_MSG_CREATEFRAME:
nRet = reinterpret_cast<LRESULT>(ImplSalCreateFrame( GetSalData()->mpFirstInstance, reinterpret_cast<HWND>(lParam), (SalFrameStyleFlags)wParam ));
@@ -639,14 +638,22 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
rDef = FALSE;
break;
case SAL_MSG_TIMER_CALLBACK:
+ {
+ WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+ assert( pTimer != nullptr );
MSG aMsg;
+ bool bValidMSG = pTimer->IsValidWPARAM( wParam );
// PM_QS_POSTMESSAGE is needed, so we don't process the SendMessage from DoYield!
- while ( PeekMessageW(&aMsg, nullptr, SAL_MSG_TIMER_CALLBACK,
+ while ( PeekMessageW(&aMsg, GetSalData()->mpFirstInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK,
SAL_MSG_TIMER_CALLBACK, PM_REMOVE | PM_NOYIELD | PM_QS_POSTMESSAGE) )
- assert(! "Multiple timer messages in queue" );
- GetSalData()->mbOnIdleRunScheduler = false;
- EmitTimerCallback();
+ {
+ assert( !bValidMSG && "Unexpected non-last valid message" );
+ bValidMSG = pTimer->IsValidWPARAM( aMsg.wParam );
+ }
+ if ( bValidMSG )
+ pTimer->ImplEmitTimerCallback();
break;
+ }
}
return nRet;
diff --git a/vcl/win/app/saltimer.cxx b/vcl/win/app/saltimer.cxx
index d57eefd63efc..10043c367883 100644
--- a/vcl/win/app/saltimer.cxx
+++ b/vcl/win/app/saltimer.cxx
@@ -31,31 +31,29 @@ static void CALLBACK SalTimerProc(PVOID pParameter, BOOLEAN bTimerOrWaitFired);
// deletion of timer (which is extremely likely, given that
// INVALID_HANDLE_VALUE waits for the callback to run on the main thread),
// this must run on the main thread too
-void ImplSalStopTimer()
+void WinSalTimer::ImplStop()
{
SalData *const pSalData = GetSalData();
- assert( !pSalData->mpFirstInstance || pSalData->mnAppThreadId == GetCurrentThreadId() );
+ const WinSalInstance *pInst = pSalData->mpFirstInstance;
+ assert( !pInst || pSalData->mnAppThreadId == GetCurrentThreadId() );
- HANDLE hTimer = pSalData->mnTimerId;
- if (hTimer)
- {
- pSalData->mnTimerId = nullptr;
- DeleteTimerQueueTimer(nullptr, hTimer, INVALID_HANDLE_VALUE);
- }
+ const HANDLE hTimer = m_nTimerId;
+ if ( nullptr == hTimer )
+ return;
- // remove all pending SAL_MSG_TIMER_CALLBACK messages
- // we always have to do this, since ImplSalStartTimer with 0ms just queues
- // a new SAL_MSG_TIMER_CALLBACK message
+ m_nTimerId = nullptr;
+ m_nTimerStartTicks = 0;
+ DeleteTimerQueueTimer( nullptr, hTimer, INVALID_HANDLE_VALUE );
+ 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;
- int nMsgCount = 0;
- while ( PeekMessageW(&aMsg, nullptr, SAL_MSG_TIMER_CALLBACK,
- SAL_MSG_TIMER_CALLBACK, PM_REMOVE | PM_NOYIELD | PM_QS_POSTMESSAGE) )
- nMsgCount++;
- assert( nMsgCount <= 1 );
+ while ( PeekMessageW(&aMsg, pInst->mhComWnd, SAL_MSG_TIMER_CALLBACK,
+ SAL_MSG_TIMER_CALLBACK, PM_REMOVE | PM_NOYIELD | PM_QS_POSTMESSAGE) );
}
-void ImplSalStartTimer( sal_uLong nMS )
+void WinSalTimer::ImplStart( sal_uLong nMS )
{
SalData* pSalData = GetSalData();
assert( !pSalData->mpFirstInstance || pSalData->mnAppThreadId == GetCurrentThreadId() );
@@ -65,17 +63,26 @@ void ImplSalStartTimer( sal_uLong nMS )
nMS = SAL_MAX_UINT32;
// cannot change a one-shot timer, so delete it and create a new one
- ImplSalStopTimer();
+ ImplStop();
- // keep the scheduler running, if a 0ms timer / Idle is scheduled
- pSalData->mbOnIdleRunScheduler = ( 0 == nMS );
+ // keep the yield running, if a 0ms Idle is scheduled
+ m_bPollForMessage = ( 0 == nMS );
+ m_nTimerStartTicks = osl_getMonotonicTicks() % SAL_MAX_UINT32;
// probably WT_EXECUTEONLYONCE is not needed, but it enforces Period
// to be 0 and should not hurt; also see
// https://www.microsoft.com/msj/0499/pooling/pooling.aspx
- CreateTimerQueueTimer(&pSalData->mnTimerId, nullptr, SalTimerProc, nullptr,
+ CreateTimerQueueTimer(&m_nTimerId, nullptr, SalTimerProc,
+ (void*) m_nTimerStartTicks,
nMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
}
+WinSalTimer::WinSalTimer()
+ : m_nTimerId( nullptr )
+ , m_nTimerStartTicks( 0 )
+ , m_bPollForMessage( false )
+{
+}
+
WinSalTimer::~WinSalTimer()
{
Stop();
@@ -83,28 +90,28 @@ WinSalTimer::~WinSalTimer()
void WinSalTimer::Start( sal_uLong nMS )
{
- SalData* pSalData = GetSalData();
- if ( pSalData->mpFirstInstance && pSalData->mnAppThreadId != GetCurrentThreadId() )
+ WinSalInstance *pInst = GetSalData()->mpFirstInstance;
+ if ( pInst && !pInst->IsMainThread() )
{
- BOOL const ret = PostMessageW(pSalData->mpFirstInstance->mhComWnd,
- SAL_MSG_STARTTIMER, 0, (LPARAM)GetTickCount() + nMS);
+ BOOL const ret = PostMessageW(pInst->mhComWnd,
+ SAL_MSG_STARTTIMER, 0, (LPARAM) tools::Time::GetSystemTicks() + nMS);
SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
}
else
- ImplSalStartTimer( nMS );
+ ImplStart( nMS );
}
void WinSalTimer::Stop()
{
- SalData* pSalData = GetSalData();
- if ( pSalData->mpFirstInstance && pSalData->mnAppThreadId != GetCurrentThreadId() )
+ WinSalInstance *pInst = GetSalData()->mpFirstInstance;
+ if ( pInst && !pInst->IsMainThread() )
{
- BOOL const ret = PostMessageW(pSalData->mpFirstInstance->mhComWnd,
+ BOOL const ret = PostMessageW(pInst->mhComWnd,
SAL_MSG_STOPTIMER, 0, 0);
SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
}
else
- ImplSalStopTimer();
+ ImplStop();
}
/** This gets invoked from a Timer Queue thread.
@@ -112,20 +119,18 @@ void WinSalTimer::Stop()
Don't acquire the SolarMutex to avoid deadlocks, just wake up the main thread
at better resolution than 10ms.
*/
-static void CALLBACK SalTimerProc(PVOID, BOOLEAN)
+static void CALLBACK SalTimerProc(PVOID data, BOOLEAN)
{
__try
{
- SalData* pSalData = GetSalData();
-
// always post message when the timer fires, we will remove the ones
// that happened during execution of the callback later directly from
// the message queue
- BOOL const ret = PostMessageW(pSalData->mpFirstInstance->mhComWnd,
- SAL_MSG_TIMER_CALLBACK, 0, 0);
+ BOOL const ret = PostMessageW(GetSalData()->mpFirstInstance->mhComWnd,
+ SAL_MSG_TIMER_CALLBACK, (WPARAM) data, 0);
#if OSL_DEBUG_LEVEL > 0
if (0 == ret) // SEH prevents using SAL_WARN here?
- fputs("ERROR: PostMessage() failed!", stderr);
+ fputs("ERROR: PostMessage() failed!\n", stderr);
#endif
}
__except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
@@ -133,22 +138,14 @@ static void CALLBACK SalTimerProc(PVOID, BOOLEAN)
}
}
-/** 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.
-*/
-void EmitTimerCallback()
+void WinSalTimer::ImplEmitTimerCallback()
{
// Test for MouseLeave
SalTestMouseLeave();
- ImplSVData *pSVData = ImplGetSVData();
- if ( ! pSVData->maSchedCtx.mpSalTimer )
- return;
-
+ m_bPollForMessage = false;
ImplSalYieldMutexAcquireWithWait();
- pSVData->maSchedCtx.mpSalTimer->CallCallback();
+ CallCallback();
ImplSalYieldMutexRelease();
}
commit 6d3af1fbcb45069ff23548b59b591ae39d361a69
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Thu Aug 24 18:54:21 2017 +0200
Don't run the OLEObjCache timer for an empty cache
Change-Id: I210f6bdec14491bea6d15bca133011059091f21b
diff --git a/svx/source/svdraw/svdetc.cxx b/svx/source/svdraw/svdetc.cxx
index 9cc6708020bb..61a9fbb5c797 100644
--- a/svx/source/svdraw/svdetc.cxx
+++ b/svx/source/svdraw/svdetc.cxx
@@ -113,8 +113,6 @@ OLEObjCache::OLEObjCache()
pTimer->SetInvokeHandler( LINK(this, OLEObjCache, UnloadCheckHdl) );
pTimer->SetTimeout(20000);
pTimer->SetStatic();
- pTimer->Invoke();
- pTimer->Start();
}
OLEObjCache::~OLEObjCache()
@@ -196,6 +194,9 @@ void OLEObjCache::InsertObj(SdrOle2Obj* pObj)
// a new object was inserted, recalculate the cache
UnloadOnDemand();
}
+
+ if ( !pTimer->IsActive() )
+ pTimer->Start();
}
void OLEObjCache::RemoveObj(SdrOle2Obj* pObj)
@@ -203,6 +204,8 @@ void OLEObjCache::RemoveObj(SdrOle2Obj* pObj)
std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
if (it != maObjs.end())
maObjs.erase(it);
+ if (maObjs.empty())
+ pTimer->Stop();
}
size_t OLEObjCache::size() const
commit 852dbed88b889af770f3ae17d5e737b663e4fd81
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Wed Aug 23 16:07:50 2017 +0200
Workaround static Task destruction error
A task has to get the SchedulerLock to remove itself from the
Scheduler list. This doesn't work, if the Task is static, as the
static Scheduler might be destroyed earlier. In this case we fail
with the following backtrace:
#0 SchedulerMutex::acquire
#1 Task::~Task
#2 __run_exit_handlers
Thanks to Michael Stahl to catching this backtrace.
As a workaround this marks static tasks, so they ignore the
SchedulerMutex in the destructor, We also mark all scheduled Tasks
as "static" in DeInitScheduler, as their cleanup was already done.
In the end all Tasks should be removed from static objects.
Change-Id: I38be3206378b9449193efaccbc96896ac8de9478
diff --git a/include/vcl/task.hxx b/include/vcl/task.hxx
index e45fe3f5ae73..2711d4343932 100644
--- a/include/vcl/task.hxx
+++ b/include/vcl/task.hxx
@@ -47,6 +47,7 @@ class VCL_DLLPUBLIC Task
const sal_Char *mpDebugName; ///< Useful for debugging
TaskPriority mePriority; ///< Task priority
bool mbActive; ///< Currently in the scheduler
+ bool mbStatic; ///< Is a static object
protected:
static void StartTimer( sal_uInt64 nMS );
@@ -88,6 +89,15 @@ public:
void Stop();
bool IsActive() const { return mbActive; }
+
+ /**
+ * This function must be called for static tasks, so the Task destructor
+ * ignores the SchedulerMutex, as it may not be available anymore.
+ * The cleanup is still correct, as it has already happened in
+ * DeInitScheduler call well before the static destructor calls.
+ */
+ void SetStatic() { mbStatic = true; }
+ bool IsStatic() const { return mbStatic; }
};
#endif // INCLUDED_VCL_TASK_HXX
diff --git a/svx/source/svdraw/svdetc.cxx b/svx/source/svdraw/svdetc.cxx
index 7ea0e0b686ba..9cc6708020bb 100644
--- a/svx/source/svdraw/svdetc.cxx
+++ b/svx/source/svdraw/svdetc.cxx
@@ -112,6 +112,7 @@ OLEObjCache::OLEObjCache()
pTimer = new AutoTimer( "svx OLEObjCache pTimer UnloadCheck" );
pTimer->SetInvokeHandler( LINK(this, OLEObjCache, UnloadCheckHdl) );
pTimer->SetTimeout(20000);
+ pTimer->SetStatic();
pTimer->Invoke();
pTimer->Start();
}
diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index 0059e6ebdc57..0cd5a24d5f83 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -172,6 +172,13 @@ Since the Scheduler is always handled by the system message queue, there is
really no more reasoning to stop after 100 events to prevent LO Scheduler
starvation.
+== Drop static inherited or composed Task objects ==
+
+The sequence of destruction of static objects is not defined. So a static Task
+can not be guaranteed to happen before the Scheduler. When dynamic unloading
+is involved, this becomes an even worse problem. This way we could drop the
+mbStatic workaround from the Task class.
+
== Run the LO application in its own thread ==
This would probably get rid of most of the MacOS and Windows implementation
diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx
index d9630ea0cca6..f1a9a43f4948 100644
--- a/vcl/source/app/scheduler.cxx
+++ b/vcl/source/app/scheduler.cxx
@@ -159,6 +159,7 @@ void Scheduler::ImplDeInitScheduler()
pTask->mbActive = false;
}
pTask->mpSchedulerData = nullptr;
+ pTask->SetStatic();
}
ImplSchedulerData* pDeleteSchedulerData = pSchedulerData;
pSchedulerData = pSchedulerData->mpNext;
@@ -545,6 +546,7 @@ Task::Task( const sal_Char *pDebugName )
, mpDebugName( pDebugName )
, mePriority( TaskPriority::DEFAULT )
, mbActive( false )
+ , mbStatic( false )
{
}
@@ -553,6 +555,7 @@ Task::Task( const Task& rTask )
, mpDebugName( rTask.mpDebugName )
, mePriority( rTask.mePriority )
, mbActive( false )
+ , mbStatic( false )
{
if ( rTask.IsActive() )
Start();
@@ -560,9 +563,14 @@ Task::Task( const Task& rTask )
Task::~Task() COVERITY_NOEXCEPT_FALSE
{
- SchedulerGuard aSchedulerGuard;
- if ( mpSchedulerData )
- mpSchedulerData->mpTask = nullptr;
+ if ( !IsStatic() )
+ {
+ SchedulerGuard aSchedulerGuard;
+ if ( mpSchedulerData )
+ mpSchedulerData->mpTask = nullptr;
+ }
+ else
+ assert( nullptr == mpSchedulerData );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salbmp.cxx b/vcl/win/gdi/salbmp.cxx
index c9216bfd0109..fb8fb10d8055 100644
--- a/vcl/win/gdi/salbmp.cxx
+++ b/vcl/win/gdi/salbmp.cxx
@@ -80,7 +80,7 @@ public:
maEntries()
{
SetTimeout(1000);
- Stop();
+ SetStatic();
}
~GdiPlusBuffer() override
commit e9c9c5f401fc12f5b39578c712fd9cade1759dde
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date: Tue Aug 8 15:03:37 2017 +0200
tdf#99784 OSX run GUI stuff in the main thread
The extension manager starts dialogs from its own thread. But the
OSX backend currently doesn't defer these calls to the main thread.
This implements the deference by running the called function
in the main thread, using a code ^Block, and returning the result
via a SalYieldMutex member.
Change-Id: Id8977991e3eda91da27c23d8021e028d4f4cefe5
diff --git a/include/vcl/pointr.hxx b/include/vcl/pointr.hxx
index c82fb2236db0..8370b9f9d296 100644
--- a/include/vcl/pointr.hxx
+++ b/include/vcl/pointr.hxx
@@ -23,8 +23,6 @@
#include <vcl/dllapi.h>
#include <vcl/ptrstyle.hxx>
-class Point;
-
class VCL_DLLPUBLIC Pointer
{
PointerStyle meStyle;
diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index 0251ab88fcab..0059e6ebdc57 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -94,7 +94,19 @@ can be added to the scheduler reasonably.
Generally the Scheduler is handled as expected, except on resize, which is
handled with different runloop-modes in MacOS. In case of a resize, the normal
runloop is suspended in sendEvent, so we can't call the scheduler via posted
-main loop-events. Instead the schedule the timer again.
+main loop-events. Instead the scheduler uses the timer again.
+
+Like the Windows backend, all Cocoa / GUI handling also has to be run in
+the main thread. We're emulating Windows out-of-order PeekMessage processing,
+via a YieldWakeupEvent and two conditionals. When in a RUNINMAIN call, all
+the DBG_TESTSOLARMUTEX calls are disabled, as we can't release the SolarMutex,
+but we can prevent running any other SolarMutex based code. Same for all the
+SolarMutex acquire and release calls, so the calling and the main thread
+don't deadlock.
+
+We can neigher rely on MacOS dispatch_sync code block execution nor the
+message handling, as both can't be priorized or filtered and the first
+does also not allow nested execution and is just processed in sequence.
There is also a workaround for a problem for pushing tasks to an empty queue,
as [NSApp postEvent: ... atStart: NO] doesn't append the event, if the
@@ -159,3 +171,8 @@ easy way to process just a single event).
Since the Scheduler is always handled by the system message queue, there is
really no more reasoning to stop after 100 events to prevent LO Scheduler
starvation.
+
+== Run the LO application in its own thread ==
+
+This would probably get rid of most of the MacOS and Windows implementation
+details / workarounds, but is quite probably a large amount of work.
diff --git a/vcl/inc/osx/runinmain.hxx b/vcl/inc/osx/runinmain.hxx
new file mode 100644
index 000000000000..e9ef9113d041
--- /dev/null
+++ b/vcl/inc/osx/runinmain.hxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_INC_OSX_RUNINMAIN_HXX
+#define INCLUDED_VCL_INC_OSX_RUNINMAIN_HXX
+
+/**
+ * Runs a command in the main thread.
+ *
+ * These macros are always used like a recursive calls, so they work like
+ * a closure.
+ *
+ * Uses two conditionals for a two way communication.
+ * The data (code block + result) is protected by the SolarMutex.
+ *
+ * There are three main macros, which act as function initializers:
+ * - OSX_RUNINMAIN - for all functions without return values
+ * - OSX_RUNINMAIN_POINTER - for all functions returning a pointer
+ * - OSX_RUNINMAIN_UNION - for all other return types
+ *
+ * All types used via OSX_RUNINMAIN_UNION must implement a move constructor,
+ * so there is no memory leak!
+ */
+
+#include <unordered_map>
+
+#include <Block.h>
+
+#include <osl/thread.h>
+
+#include "saltimer.h"
+#include "salframe.hxx"
+
+union RuninmainResult
+{
+ void* pointer;
+ bool boolean;
+ struct SalFrame::SalPointerState state;
+
+ RuninmainResult() {}
+};
+
+#define OSX_RUNINMAIN_MEMBERS \
+ osl::Condition m_aInMainCondition; \
+ osl::Condition m_aResultCondition; \
+ RuninmainBlock m_aCodeBlock; \
+ RuninmainResult m_aResult;
+
+#define OSX_RUNINMAIN( instance, command ) \
+ if ( !instance->IsMainThread() ) \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ SalYieldMutex *aMutex = static_cast<SalYieldMutex*>(instance->GetYieldMutex()); \
+ assert( !aMutex->m_aCodeBlock ); \
+ aMutex->m_aCodeBlock = Block_copy(^{ \
+ command; \
+ }); \
+ aMutex->m_aResultCondition.reset(); \
+ dispatch_async(dispatch_get_main_queue(),^{ \
+ ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO ); \
+ }); \
+ aMutex->m_aInMainCondition.set(); \
+ osl::Condition::Result res = aMutex->m_aResultCondition.wait(); \
+ assert(osl::Condition::Result::result_ok == res); \
+ return; \
+ }
+
+#define OSX_RUNINMAIN_POINTER( instance, command, type ) \
+ if ( !instance->IsMainThread() ) \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ SalYieldMutex *aMutex = static_cast<SalYieldMutex*>(instance->GetYieldMutex()); \
+ assert( !aMutex->m_aCodeBlock ); \
+ aMutex->m_aCodeBlock = Block_copy(^{ \
+ aMutex->m_aResult.pointer = static_cast<void*>( command ); \
+ }); \
+ aMutex->m_aResultCondition.reset(); \
+ dispatch_async(dispatch_get_main_queue(),^{ \
+ ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO ); \
+ }); \
+ aMutex->m_aInMainCondition.set(); \
+ osl::Condition::Result res = aMutex->m_aResultCondition.wait(); \
+ assert(osl::Condition::Result::result_ok == res); \
+ return static_cast<type>( aMutex->m_aResult.pointer ); \
+ }
+
+#define OSX_RUNINMAIN_UNION( instance, command, member ) \
+ if ( !instance->IsMainThread() ) \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ SalYieldMutex *aMutex = static_cast<SalYieldMutex*>(instance->GetYieldMutex()); \
+ assert( !aMutex->m_aCodeBlock ); \
+ aMutex->m_aCodeBlock = Block_copy(^{ \
+ aMutex->m_aResult.member = command; \
+ }); \
+ aMutex->m_aResultCondition.reset(); \
+ dispatch_async(dispatch_get_main_queue(),^{ \
+ ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO ); \
+ }); \
+ aMutex->m_aInMainCondition.set(); \
+ osl::Condition::Result res = aMutex->m_aResultCondition.wait(); \
+ assert(osl::Condition::Result::result_ok == res); \
+ return std::move( aMutex->m_aResult.member ); \
+ }
+
+/**
+ * convenience macros used from SalInstance
+ */
+
+#define OSX_INST_RUNINMAIN( command ) \
+ OSX_RUNINMAIN( this, command )
+
+#define OSX_INST_RUNINMAIN_POINTER( command, type ) \
+ OSX_RUNINMAIN_POINTER( this, command, type )
+
+#define OSX_INST_RUNINMAIN_UNION( command, member ) \
+ OSX_RUNINMAIN_UNION( this, command, member )
+
+/**
+ * convenience macros using global SalData
+ */
+
+#define OSX_SALDATA_RUNINMAIN( command ) \
+ OSX_RUNINMAIN( GetSalData()->mpFirstInstance, command )
+
+#define OSX_SALDATA_RUNINMAIN_POINTER( command, type ) \
+ OSX_RUNINMAIN_POINTER( GetSalData()->mpFirstInstance, command, type )
+
+#define OSX_SALDATA_RUNINMAIN_UNION( command, member ) \
+ OSX_RUNINMAIN_UNION( GetSalData()->mpFirstInstance, command, member )
+
+#endif // INCLUDED_VCL_INC_OSX_RUNINMAIN_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/osx/saldata.hxx b/vcl/inc/osx/saldata.hxx
index fbc28e05391d..1cbdabb88563 100644
--- a/vcl/inc/osx/saldata.hxx
+++ b/vcl/inc/osx/saldata.hxx
@@ -45,6 +45,8 @@
#include "apple_remote/RemoteMainController.h"
+#include "osx/runinmain.hxx"
+
class AquaSalInstance;
class SalObject;
class SalFrame;
diff --git a/vcl/inc/osx/salframe.h b/vcl/inc/osx/salframe.h
index 4b6d486f6be6..307356c76caa 100644
--- a/vcl/inc/osx/salframe.h
+++ b/vcl/inc/osx/salframe.h
@@ -29,6 +29,7 @@
#include "osx/salmenu.h"
#include "osx/saldata.hxx"
#include "osx/osxvcltypes.h"
+#include "osx/runinmain.hxx"
#include "salframe.hxx"
@@ -183,22 +184,22 @@ public:
void VCLToCocoa( NSPoint& io_rPoint, bool bRelativeToScreen = true );
void CocoaToVCL( NSPoint& io_Point, bool bRelativeToScreen = true );
- NSCursor* getCurrentCursor() const;
+ NSCursor* getCurrentCursor();
CGMutablePathRef getClipPath() const { return mrClippingPath; }
// called by VCL_NSApplication to indicate screen settings have changed
void screenParametersChanged();
- private: // methods
+private: // methods
/** do things on initial show (like centering on parent or on screen)
*/
void initShow();
void initWindowAndView();
- private: // data
- static AquaSalFrame* s_pCaptureFrame;
+private: // data
+ static AquaSalFrame* s_pCaptureFrame;
AquaSalFrame( const AquaSalFrame& ) = delete;
AquaSalFrame& operator=(const AquaSalFrame&) = delete;
diff --git a/vcl/inc/osx/salinst.h b/vcl/inc/osx/salinst.h
index 263b9027b956..7c47f71a3696 100644
--- a/vcl/inc/osx/salinst.h
+++ b/vcl/inc/osx/salinst.h
@@ -32,13 +32,22 @@
#endif
#include "salinst.hxx"
+#include "osx/runinmain.hxx"
+
class AquaSalFrame;
+class SalFrame;
+class SalObject;
class ApplicationEvent;
class Image;
enum class SalEvent;
+typedef void(^RuninmainBlock)(void);
+
class SalYieldMutex : public comphelper::GenericSolarMutex
{
+public:
+ OSX_RUNINMAIN_MEMBERS
+
protected:
virtual void doAcquire( sal_uInt32 nLockCount ) override;
virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
@@ -46,6 +55,8 @@ protected:
public:
SalYieldMutex();
virtual ~SalYieldMutex();
+
+ virtual bool IsCurrentThread() const override;
};
class AquaSalInstance : public SalInstance
@@ -71,6 +82,7 @@ public:
osl::Mutex maUserEventListMutex;
osl::Condition maWaitingYieldCond;
bool mbIsLiveResize;
+ bool mbNoYieldLock;
static std::list<const ApplicationEvent*> aAppEventList;
@@ -150,6 +162,7 @@ public:
static const short AppStartTimerEvent = 10;
static const short YieldWakeupEvent = 20;
static const short DispatchTimerEvent = 30;
+ static const short PostedUserEvent = 40;
static NSMenu* GetDynamicDockMenu();
};
diff --git a/vcl/osx/a11ytextwrapper.mm b/vcl/osx/a11ytextwrapper.mm
index a46ffd6e7fd9..14a63aa8fd9e 100644
--- a/vcl/osx/a11ytextwrapper.mm
+++ b/vcl/osx/a11ytextwrapper.mm
@@ -203,8 +203,8 @@ using namespace ::com::sun::star::uno;
+(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point {
NSValue * value = nil;
- Point aPoint( [ AquaA11yUtil nsPointToVclPoint: point ]);
- const Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen();
+ css::awt::Point aPoint( [ AquaA11yUtil nsPointToVclPoint: point ]);
+ const css::awt::Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen();
aPoint.X -= screenPos.X;
aPoint.Y -= screenPos.Y;
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list