[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