This is an automated email from the ASF dual-hosted git repository.

jimjag pushed a commit to branch AOO50X
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit 33d22be1ab634eb5ed0cbab2d3104438b847f927
Author: Jim Jagielski <[email protected]>
AuthorDate: Thu Oct 22 08:01:51 2026 -0400

    macOS: run the AppKit menu and window calls from UNO threads on AOO's main 
thread
    
    macOS AppKit throws or blocks on menu and window changes made off the main 
thread, so
    remote UNO clients crashed soffice on load and close, and failed on the 
first edit.
    
    (cherry picked from commit 2a4643a67e92c4338a0f7b947e78b04714015a0d)
---
 main/vcl/aqua/source/app/salinst.cxx     | 225 ++++++++++++++++++++++++++++---
 main/vcl/aqua/source/window/salframe.cxx |  68 ++++++++++
 main/vcl/aqua/source/window/salmenu.cxx  |  74 +++++++++-
 main/vcl/inc/aqua/salinst.h              |  46 +++++++
 4 files changed, 393 insertions(+), 20 deletions(-)

diff --git a/main/vcl/aqua/source/app/salinst.cxx 
b/main/vcl/aqua/source/app/salinst.cxx
index 0cc37adeb7..1d2288e3f3 100644
--- a/main/vcl/aqua/source/app/salinst.cxx
+++ b/main/vcl/aqua/source/app/salinst.cxx
@@ -25,6 +25,7 @@
 #include "precompiled_vcl.hxx"
 
 #include <stdio.h>
+#include <utility>
 
 #include "tools/fsys.hxx"
 #include "tools/getprocessworkingdir.hxx"
@@ -339,21 +340,159 @@ void DeInitSalMain()
 
 // =======================================================================
 
+static void postYieldWakeupEvent()
+{
+    if( ! NSApp )
+        return;
+    SalData::ensureThreadAutoreleasePool();
+    NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
+                               location: NSZeroPoint
+                               modifierFlags: 0
+                               timestamp: 0
+                               windowNumber: 0
+                               context: nil
+                               subtype: AquaSalInstance::YieldWakeupEvent
+                               data1: 0
+                               data2: 0 ];
+    if( pEvent )
+        [NSApp postEvent: pEvent atStart: NO];
+}
+
+@interface SalMainThreadRunner : NSObject
+{
+}
+-(void)runPending: (id)pUnused;
+@end
+
+@implementation SalMainThreadRunner
+-(void)runPending: (id)pUnused
+{
+    (void)pUnused;
+    GetSalData()->mpFirstInstance->mpSalYieldMutex->runPendingOnMainThread();
+}
+@end
+
 SalYieldMutex::SalYieldMutex()
 {
        mnCount  = 0;
        mnThreadId  = 0;
+    mnMainThreadId = 0;
+    pthread_mutex_init( &maMainThreadMutex, NULL );
+    pthread_cond_init( &maMainThreadCond, NULL );
+    pthread_cond_init( &maResultCond, NULL );
+    mbWakeUpMain = false;
+    mbMainWaiting = false;
+    mbResultReady = false;
+    mpMainThreadFunc = NULL;
+    mbRunningForOwner = false;
+}
+
+SalYieldMutex::~SalYieldMutex()
+{
+    pthread_cond_destroy( &maResultCond );
+    pthread_cond_destroy( &maMainThreadCond );
+    pthread_mutex_destroy( &maMainThreadMutex );
+}
+
+bool SalYieldMutex::isMainThread() const
+{
+    return mnMainThreadId != 0 && vos::OThread::getCurrentIdentifier() == 
mnMainThreadId;
 }
 
 void SalYieldMutex::acquire()
 {
-       OMutex::acquire();
+    if( isMainThread() )
+    {
+        if( mbRunningForOwner )
+            return;
+        acquireOnMainThread();
+    }
+    else
+        OMutex::acquire();
        mnThreadId = vos::OThread::getCurrentIdentifier();
        mnCount++;
 }
 
+void SalYieldMutex::acquireOnMainThread()
+{
+    // while another thread owns the mutex, run what it hands over via 
runOnMainThread()
+    for(;;)
+    {
+        pthread_mutex_lock( &maMainThreadMutex );
+        if( OMutex::tryToAcquire() )
+        {
+            mbWakeUpMain = false;
+            pthread_mutex_unlock( &maMainThreadMutex );
+            return;
+        }
+        mbMainWaiting = true;
+        while( ! mbWakeUpMain )
+            pthread_cond_wait( &maMainThreadCond, &maMainThreadMutex );
+        mbMainWaiting = false;
+        mbWakeUpMain = false;
+        const boost::function0<void>* pFunc = mpMainThreadFunc;
+        mpMainThreadFunc = NULL;
+        pthread_mutex_unlock( &maMainThreadMutex );
+
+        if( pFunc )
+            runForOwner( pFunc );
+    }
+}
+
+void SalYieldMutex::runPendingOnMainThread()
+{
+    if( ! isMainThread() || mbRunningForOwner )
+        return;
+
+    pthread_mutex_lock( &maMainThreadMutex );
+    const boost::function0<void>* pFunc = mpMainThreadFunc;
+    mpMainThreadFunc = NULL;
+    pthread_mutex_unlock( &maMainThreadMutex );
+
+    if( pFunc )
+        runForOwner( pFunc );
+}
+
+void SalYieldMutex::runForOwner( const boost::function0<void>* pFunc )
+{
+    std::exception_ptr aException;
+    mbRunningForOwner = true;
+    try
+    {
+        (*pFunc)();
+    }
+    catch( ... )
+    {
+        aException = std::current_exception();
+    }
+    mbRunningForOwner = false;
+
+    pthread_mutex_lock( &maMainThreadMutex );
+    maMainThreadException = aException;
+    mbResultReady = true;
+    pthread_cond_signal( &maResultCond );
+    pthread_mutex_unlock( &maMainThreadMutex );
+}
+
 void SalYieldMutex::release()
 {
+    const bool bMainThread = isMainThread();
+    if( bMainThread && mbRunningForOwner )
+        return;
+
+    // the main thread may be waiting for this release in acquireOnMainThread()
+    if( ! bMainThread && mnCount == 1 && mnThreadId == 
vos::OThread::getCurrentIdentifier() )
+    {
+        pthread_mutex_lock( &maMainThreadMutex );
+        mnThreadId = 0;
+        mnCount--;
+        OMutex::release();
+        mbWakeUpMain = true;
+        pthread_cond_signal( &maMainThreadCond );
+        pthread_mutex_unlock( &maMainThreadMutex );
+        return;
+    }
+
        if ( mnThreadId == vos::OThread::getCurrentIdentifier() )
        {
                if ( mnCount == 1 )
@@ -363,8 +502,56 @@ void SalYieldMutex::release()
        OMutex::release();
 }
 
+void SalYieldMutex::runOnMainThread( const boost::function0<void>& rFunc )
+{
+    if( isMainThread() )
+    {
+        rFunc();
+        return;
+    }
+
+    // the main thread only runs rFunc for the owner of the mutex
+    vos::OGuard aGuard( *this );
+
+    bool bMainWaiting;
+    pthread_mutex_lock( &maMainThreadMutex );
+    mpMainThreadFunc = &rFunc;
+    mbWakeUpMain = true;
+    bMainWaiting = mbMainWaiting;
+    pthread_cond_signal( &maMainThreadCond );
+    pthread_mutex_unlock( &maMainThreadMutex );
+
+    // common modes also reach a main thread inside AppKit's menu tracking or 
live resize loops
+    if( ! bMainWaiting )
+    {
+        static SalMainThreadRunner* pRunner = [[SalMainThreadRunner alloc] 
init];
+        [pRunner performSelectorOnMainThread: @selector(runPending:)
+                 withObject: nil
+                 waitUntilDone: NO
+                 modes: [NSArray arrayWithObject: NSRunLoopCommonModes]];
+    }
+
+    std::exception_ptr aException;
+    pthread_mutex_lock( &maMainThreadMutex );
+    while( ! mbResultReady )
+        pthread_cond_wait( &maResultCond, &maMainThreadMutex );
+    mbResultReady = false;
+    std::swap( aException, maMainThreadException );
+    pthread_mutex_unlock( &maMainThreadMutex );
+
+    if( aException )
+        std::rethrow_exception( aException );
+}
+
+bool SalYieldMutex::isRunningForOwner() const
+{
+    return isMainThread() && mbRunningForOwner;
+}
+
 sal_Bool SalYieldMutex::tryToAcquire()
 {
+    if( isMainThread() && mbRunningForOwner )
+        return sal_True;
        if ( OMutex::tryToAcquire() )
        {
                mnThreadId = vos::OThread::getCurrentIdentifier();
@@ -444,10 +631,11 @@ void DestroySalInstance( SalInstance* pInst )
 
 AquaSalInstance::AquaSalInstance()
 {
+    maMainThread = vos::OThread::getCurrentIdentifier();
        mpSalYieldMutex = new SalYieldMutex;
+    mpSalYieldMutex->setMainThread( maMainThread );
        mpSalYieldMutex->acquire();
        ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex );
-    maMainThread = vos::OThread::getCurrentIdentifier();
     mbWaitingYield = false;
     maUserEventListMutex = osl_createMutex();
     mnActivePrintJobs = 0;
@@ -471,20 +659,7 @@ void AquaSalInstance::wakeupYield()
 {
     // wakeup :Yield
     if( mbWaitingYield )
-    {
-        SalData::ensureThreadAutoreleasePool();
-        NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
-                                   location: NSZeroPoint
-                                   modifierFlags: 0
-                                   timestamp: 0
-                                   windowNumber: 0
-                                   context: nil
-                                   subtype: AquaSalInstance::YieldWakeupEvent
-                                   data1: 0
-                                   data2: 0 ];
-        if( pEvent )
-            [NSApp postEvent: pEvent atStart: NO];
-    }
+        postYieldWakeupEvent();
 }
 
 // -----------------------------------------------------------------------
@@ -546,6 +721,9 @@ bool AquaSalInstance::CheckYieldMutex()
 {
     bool bRet = true;
 
+    if( mpSalYieldMutex->isRunningForOwner() )
+        return bRet;
+
        SalYieldMutex* pYieldMutex = mpSalYieldMutex;
        if ( pYieldMutex->GetThreadId() !=
                 vos::OThread::getCurrentIdentifier() )
@@ -565,6 +743,16 @@ bool AquaSalInstance::isNSAppThread() const
 
 // -----------------------------------------------------------------------
 
+bool AquaSalInstance::forwardToMainThread( const boost::function0<void>& rFunc 
)
+{
+    if( isNSAppThread() )
+        return false;
+    mpSalYieldMutex->runOnMainThread( rFunc );
+    return true;
+}
+
+// -----------------------------------------------------------------------
+
 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
 {
     switch( [pEvent subtype] )
@@ -820,6 +1008,9 @@ void AquaSalInstance::Yield( bool bWait, bool 
bHandleAllCurrentEvents )
 
 bool AquaSalInstance::AnyInput( sal_uInt16 nType )
 {
+    if( ! isNSAppThread() )
+        return callOnMainThread< bool >( boost::bind( 
&AquaSalInstance::AnyInput, this, nType ) );
+
     if( nType & INPUT_APPEVENT )
     {
         if( ! aAppEventList.empty() )
@@ -883,6 +1074,8 @@ SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, 
sal_uLong nSalFrameSt
 
 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
 {
+    if( forwardToMainThread( boost::bind( &AquaSalInstance::DestroyFrame, 
this, pFrame ) ) )
+        return;
        delete pFrame;
 }
 
diff --git a/main/vcl/aqua/source/window/salframe.cxx 
b/main/vcl/aqua/source/window/salframe.cxx
index 8638e68e8b..af99c4c153 100644
--- a/main/vcl/aqua/source/window/salframe.cxx
+++ b/main/vcl/aqua/source/window/salframe.cxx
@@ -301,6 +301,9 @@ sal_Bool AquaSalFrame::PostEvent( void *pData )
 // -----------------------------------------------------------------------
 void AquaSalFrame::SetTitle(const XubString& rTitle)
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetTitle, this, boost::cref( rTitle ) ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -345,6 +348,9 @@ void AquaSalFrame::SetIcon( sal_uInt16 )
 
 void AquaSalFrame::SetRepresentedURL( const rtl::OUString& i_rDocURL )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetRepresentedURL, this, boost::cref( i_rDocURL ) ) ) )
+        return;
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -418,6 +424,9 @@ void AquaSalFrame::SendPaintEvent( const Rectangle* pRect )
 
 void AquaSalFrame::Show(sal_Bool bVisible, sal_Bool bNoActivate)
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::Show, this, bVisible, bNoActivate ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -487,6 +496,9 @@ void AquaSalFrame::Enable( sal_Bool )
 
 void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetMinClientSize, this, nWidth, nHeight ) ) )
+        return;
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -512,6 +524,9 @@ void AquaSalFrame::SetMinClientSize( long nWidth, long 
nHeight )
 
 void AquaSalFrame::SetMaxClientSize( long nWidth, long nHeight )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetMaxClientSize, this, nWidth, nHeight ) ) )
+        return;
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -541,6 +556,9 @@ void AquaSalFrame::SetMaxClientSize( long nWidth, long 
nHeight )
 
 void AquaSalFrame::SetClientSize( long nWidth, long nHeight )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetClientSize, this, nWidth, nHeight ) ) )
+        return;
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -576,6 +594,9 @@ void AquaSalFrame::GetClientSize( long& rWidth, long& 
rHeight )
 
 void AquaSalFrame::SetWindowState( const SalFrameState* pState )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetWindowState, this, pState ) ) )
+        return;
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -652,6 +673,10 @@ void AquaSalFrame::SetWindowState( const SalFrameState* 
pState )
 
 sal_Bool AquaSalFrame::GetWindowState( SalFrameState* pState )
 {
+    AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+    if( ! pInst->isNSAppThread() )
+        return pInst->callOnMainThread< sal_Bool >( boost::bind( 
&AquaSalFrame::GetWindowState, this, pState ) );
+
     if ( !mpNSWindow )
         return FALSE;
 
@@ -692,6 +717,9 @@ sal_Bool AquaSalFrame::GetWindowState( SalFrameState* 
pState )
 
 void AquaSalFrame::SetScreenNumber(unsigned int nScreen)
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetScreenNumber, this, nScreen ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -729,6 +757,9 @@ void AquaSalFrame::SetScreenNumber(unsigned int nScreen)
 
 void AquaSalFrame::ShowFullScreen( sal_Bool bFullScreen, sal_Int32 nDisplay )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::ShowFullScreen, this, bFullScreen, nDisplay ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -842,6 +873,9 @@ public:
 
 void AquaSalFrame::StartPresentation( sal_Bool bStart )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::StartPresentation, this, bStart ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -874,6 +908,9 @@ void AquaSalFrame::SetAlwaysOnTop( sal_Bool )
 
 void AquaSalFrame::ToTop(sal_uInt16 nFlags)
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::ToTop, this, nFlags ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -936,6 +973,9 @@ NSCursor* AquaSalFrame::getCurrentCursor() const
 
 void AquaSalFrame::SetPointer( PointerStyle ePointerStyle )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetPointer, this, ePointerStyle ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -1237,6 +1277,9 @@ void AquaSalFrame::getResolution( sal_Int32& o_rDPIX, 
sal_Int32& o_rDPIY )
 // doesn't make the anything cleaner for now
 void AquaSalFrame::UpdateSettings( AllSettings& rSettings )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::UpdateSettings, this, boost::ref( rSettings ) ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -1354,6 +1397,9 @@ void AquaSalFrame::Beep( SoundType eSoundType )
 
 void AquaSalFrame::SetPosSize(long nX, long nY, long nWidth, long nHeight, 
sal_uInt16 nFlags)
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetPosSize, this, nX, nY, nWidth, nHeight, nFlags ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -1444,6 +1490,9 @@ void AquaSalFrame::SetPosSize(long nX, long nY, long 
nWidth, long nHeight, sal_u
 
 void AquaSalFrame::GetWorkArea( Rectangle& rRect )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::GetWorkArea, this, boost::ref( rRect ) ) ) )
+        return;
+
     if ( !mpNSWindow )
         return;
 
@@ -1463,6 +1512,10 @@ void AquaSalFrame::GetWorkArea( Rectangle& rRect )
 
 SalPointerState        AquaSalFrame::GetPointerState()
 {
+    AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+    if( ! pInst->isNSAppThread() )
+        return pInst->callOnMainThread< SalPointerState >( boost::bind( 
&AquaSalFrame::GetPointerState, this ) );
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -1573,6 +1626,9 @@ void AquaSalFrame::DrawMenuBar()
 
 void AquaSalFrame::SetMenu( SalMenu* pSalMenu )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetMenu, this, pSalMenu ) ) )
+        return;
+
     // #i113170# may not be the main thread if called from UNO API
     SalData::ensureThreadAutoreleasePool();
 
@@ -1585,6 +1641,9 @@ void AquaSalFrame::SetMenu( SalMenu* pSalMenu )
 
 void AquaSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::SetExtendedFrameStyle, this, nStyle ) ) )
+        return;
+
     if ( mpNSWindow )
     {
     // #i113170# may not be the main thread if called from UNO API
@@ -1701,6 +1760,9 @@ void AquaSalFrame::CaptureMouse( sal_Bool bCapture )
 
 void AquaSalFrame::ResetClipRegion()
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::ResetClipRegion, this ) ) )
+        return;
+
     if ( !mpNSWindow )
     {
         return;
@@ -1724,6 +1786,9 @@ void AquaSalFrame::ResetClipRegion()
 
 void AquaSalFrame::BeginSetClipRegion( sal_uLong nRects )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::BeginSetClipRegion, this, nRects ) ) )
+        return;
+
     if ( !mpNSWindow )
     {
         return;
@@ -1763,6 +1828,9 @@ void AquaSalFrame::UnionClipRegion( long nX, long nY, 
long nWidth, long nHeight
 
 void AquaSalFrame::EndSetClipRegion()
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalFrame::EndSetClipRegion, this ) ) )
+        return;
+
     if ( !mpNSWindow )
     {
         return;
diff --git a/main/vcl/aqua/source/window/salmenu.cxx 
b/main/vcl/aqua/source/window/salmenu.cxx
index 5f66d7e460..a33db1f963 100644
--- a/main/vcl/aqua/source/window/salmenu.cxx
+++ b/main/vcl/aqua/source/window/salmenu.cxx
@@ -98,6 +98,8 @@ static MainMenuSelector* pMainMenuSelector = nil;
 static void initAppMenu()
 {
     static bool bOnce = true;
+    if( bOnce && GetSalData()->mpFirstInstance->forwardToMainThread( 
&initAppMenu ) )
+        return;
     if( bOnce )
     {
         bOnce = false;
@@ -209,6 +211,9 @@ static void initAppMenu()
 
 SalMenu* AquaSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* pVCLMenu )
 {
+    if( ! isNSAppThread() )
+        return callOnMainThread< SalMenu* >( boost::bind( 
&AquaSalInstance::CreateMenu, this, bMenuBar, pVCLMenu ) );
+
     initAppMenu();
 
     AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar );
@@ -219,11 +224,16 @@ SalMenu* AquaSalInstance::CreateMenu( sal_Bool bMenuBar, 
Menu* pVCLMenu )
 
 void AquaSalInstance::DestroyMenu( SalMenu* pSalMenu )
 {
+    if( forwardToMainThread( boost::bind( &AquaSalInstance::DestroyMenu, this, 
pSalMenu ) ) )
+        return;
     delete pSalMenu;
 }
 
 SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData )
 {
+    if( ! isNSAppThread() )
+        return callOnMainThread< SalMenuItem* >( boost::bind( 
&AquaSalInstance::CreateMenuItem, this, pItemData ) );
+
     if( !pItemData )
         return NULL;
 
@@ -234,6 +244,8 @@ SalMenuItem* AquaSalInstance::CreateMenuItem( const 
SalItemParams* pItemData )
 
 void AquaSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
 {
+    if( forwardToMainThread( boost::bind( &AquaSalInstance::DestroyMenuItem, 
this, pSalMenuItem ) ) )
+        return;
     delete pSalMenuItem;
 }
 
@@ -341,6 +353,10 @@ sal_Int32 removeUnusedItemsRunner(NSMenu * pMenu)
 
 bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& 
rRect, sal_uLong nFlags)
 {
+    AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+    if( ! pInst->isNSAppThread() )
+        return pInst->callOnMainThread< bool >( boost::bind( 
&AquaSalMenu::ShowNativePopupMenu, this, pWin, boost::cref( rRect ), nFlags ) );
+
     // do not use native popup menu when AQUA_NATIVE_MENUS is set to sal_False
     if( ! VisibleMenuBar() ) {
         return false;
@@ -423,6 +439,9 @@ void AquaSalMenu::unsetMainMenu()
 
 void AquaSalMenu::setMainMenu()
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::setMainMenu, this ) ) )
+        return;
+
     DBG_ASSERT( mbMenuBar, "setMainMenu on non menubar" );
     if( mbMenuBar )
     {
@@ -446,6 +465,9 @@ void AquaSalMenu::setMainMenu()
 
 void AquaSalMenu::setDefaultMenu()
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( 
&AquaSalMenu::setDefaultMenu ) )
+        return;
+
     NSMenu* pMenu = [NSApp mainMenu];
 
     unsetMainMenu();
@@ -462,6 +484,9 @@ void AquaSalMenu::setDefaultMenu()
 
 void AquaSalMenu::enableMainMenu( bool bEnable )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::enableMainMenu, bEnable ) ) )
+        return;
+
     NSMenu* pMainMenu = [NSApp mainMenu];
     if( pMainMenu )
     {
@@ -477,6 +502,9 @@ void AquaSalMenu::enableMainMenu( bool bEnable )
 
 void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::addFallbackMenuItem, pNewItem ) ) )
+        return;
+
     initAppMenu();
 
     std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
@@ -499,6 +527,9 @@ void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem 
)
 
 void AquaSalMenu::removeFallbackMenuItem( NSMenuItem* pOldItem )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::removeFallbackMenuItem, pOldItem ) ) )
+        return;
+
     std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
 
     // find item
@@ -542,6 +573,9 @@ void AquaSalMenu::SetFrame( const SalFrame *pFrame )
 
 void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::InsertItem, this, pSalMenuItem, nPos ) ) )
+        return;
+
     AquaSalMenuItem *pAquaSalMenuItem = 
static_cast<AquaSalMenuItem*>(pSalMenuItem);
 
     pAquaSalMenuItem->mpParentMenu = this;
@@ -568,6 +602,9 @@ void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, 
unsigned nPos )
 
 void AquaSalMenu::RemoveItem( unsigned nPos )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::RemoveItem, this, nPos ) ) )
+        return;
+
     AquaSalMenuItem* pRemoveItem = NULL;
     if( nPos == MENU_APPEND || nPos == (maItems.size()-1) )
     {
@@ -591,8 +628,11 @@ void AquaSalMenu::RemoveItem( unsigned nPos )
         [mpMenu removeItemAtIndex: getItemIndexByPos(nPos)];
 }
 
-void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, 
unsigned /*nPos*/ )
+void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, 
unsigned nPos )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::SetSubMenu, this, pSalMenuItem, pSubMenu, nPos ) ) )
+        return;
+
     AquaSalMenuItem *pAquaSalMenuItem = 
static_cast<AquaSalMenuItem*>(pSalMenuItem);
     AquaSalMenu *subAquaSalMenu = static_cast<AquaSalMenu*>(pSubMenu);
 
@@ -633,6 +673,9 @@ void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, 
SalMenu* pSubMenu, unsi
 
 void AquaSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::CheckItem, this, nPos, bCheck ) ) )
+        return;
+
     if( nPos < maItems.size() )
     {
         NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
@@ -642,6 +685,9 @@ void AquaSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck 
)
 
 void AquaSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::EnableItem, this, nPos, bEnable ) ) )
+        return;
+
     if( nPos < maItems.size() )
     {
         NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
@@ -649,8 +695,11 @@ void AquaSalMenu::EnableItem( unsigned nPos, sal_Bool 
bEnable )
     }
 }
 
-void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSMI, const 
Image& rImage )
+void AquaSalMenu::SetItemImage( unsigned nPos, SalMenuItem* pSMI, const Image& 
rImage )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::SetItemImage, this, nPos, pSMI, boost::cref( rImage ) ) ) )
+        return;
+
     AquaSalMenuItem* pSalMenuItem = static_cast<AquaSalMenuItem*>( pSMI );
     if( ! pSalMenuItem || ! pSalMenuItem->mpMenuItem )
         return;
@@ -662,8 +711,11 @@ void AquaSalMenu::SetItemImage( unsigned /*nPos*/, 
SalMenuItem* pSMI, const Imag
         [pImage release];
 }
 
-void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem* 
i_pSalMenuItem, const XubString& i_rText )
+void AquaSalMenu::SetItemText( unsigned i_nPos, SalMenuItem* i_pSalMenuItem, 
const XubString& i_rText )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::SetItemText, this, i_nPos, i_pSalMenuItem, boost::cref( i_rText ) 
) ) )
+        return;
+
     if (!i_pSalMenuItem)
         return;
 
@@ -699,8 +751,11 @@ void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, 
SalMenuItem* i_pSalMenuItem,
     }
 }
 
-void AquaSalMenu::SetAccelerator( unsigned /*nPos*/, SalMenuItem* 
pSalMenuItem, const KeyCode& rKeyCode, const XubString& /*rKeyName*/ )
+void AquaSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, 
const KeyCode& rKeyCode, const XubString& rKeyName )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::SetAccelerator, this, nPos, pSalMenuItem, boost::cref( rKeyCode 
), boost::cref( rKeyName ) ) ) )
+        return;
+
     sal_uInt16 nModifier;
     sal_Unicode nCommandKey = 0;
 
@@ -824,6 +879,10 @@ void AquaSalMenu::releaseButtonEntry( MenuBarButtonEntry& 
i_rEntry )
 
 bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem )
 {
+    AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+    if( ! pInst->isNSAppThread() )
+        return pInst->callOnMainThread< bool >( boost::bind( 
&AquaSalMenu::AddMenuBarButton, this, boost::cref( i_rNewItem ) ) );
+
     if( ! mbMenuBar || ! VisibleMenuBar() )
         return false;
 
@@ -854,6 +913,9 @@ bool AquaSalMenu::AddMenuBarButton( const 
SalMenuButtonItem& i_rNewItem )
 
 void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId )
 {
+    if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( 
&AquaSalMenu::RemoveMenuBarButton, this, i_nId ) ) )
+        return;
+
     MenuBarButtonEntry* pEntry = findButtonItem( i_nId );
     if( pEntry )
     {
@@ -868,6 +930,10 @@ void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId )
 
 Rectangle AquaSalMenu::GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId, 
SalFrame* i_pReferenceFrame )
 {
+    AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+    if( ! pInst->isNSAppThread() )
+        return pInst->callOnMainThread< Rectangle >( boost::bind( 
&AquaSalMenu::GetMenuBarButtonRectPixel, this, i_nItemId, i_pReferenceFrame ) );
+
     if( GetSalData()->mnSystemVersion < OSX_VER_LEOPARD )
         return Rectangle( Point( -1, -1 ), Size( 1, 1 ) );
 
diff --git a/main/vcl/inc/aqua/salinst.h b/main/vcl/inc/aqua/salinst.h
index 37a4569848..927e737dd4 100644
--- a/main/vcl/inc/aqua/salinst.h
+++ b/main/vcl/inc/aqua/salinst.h
@@ -32,7 +32,13 @@
 
 #include "salinst.hxx"
 
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/ref.hpp>
+
+#include <exception>
 #include <list>
+#include <pthread.h>
 
 class AquaSalFrame;
 class ApplicationEvent;
@@ -46,14 +52,39 @@ class SalYieldMutex : public vos::OMutex
 {
        sal_uLong                                                               
                mnCount;
        vos::OThread::TThreadIdentifier             mnThreadId;
+    vos::OThread::TThreadIdentifier             mnMainThreadId;
+
+    // state for running code on the main thread on behalf of the owner
+    pthread_mutex_t                             maMainThreadMutex;
+    pthread_cond_t                              maMainThreadCond;
+    pthread_cond_t                              maResultCond;
+    bool                                        mbWakeUpMain;
+    bool                                        mbMainWaiting;
+    bool                                        mbResultReady;
+    const boost::function0<void>*               mpMainThreadFunc;
+    std::exception_ptr                          maMainThreadException;
+    bool                                        mbRunningForOwner;  // main 
thread only
+
+    bool                                        isMainThread() const;
+    void                                        acquireOnMainThread();
+    void                                        runForOwner( const 
boost::function0<void>* pFunc );
 
 public:
                                                                                
                SalYieldMutex();
+                                                ~SalYieldMutex();
        virtual void                                                            
acquire();
        virtual void                                                            
release();
        virtual sal_Bool                                                        
tryToAcquire();
        sal_uLong                                                               
                GetAcquireCount() const { return mnCount; }
        vos::OThread::TThreadIdentifier             GetThreadId() const { 
return mnThreadId; }
+
+    void                                        setMainThread( 
vos::OThread::TThreadIdentifier nId ) { mnMainThreadId = nId; }
+    // runs rFunc on the main thread while the calling thread keeps owning the 
mutex
+    void                                        runOnMainThread( const 
boost::function0<void>& rFunc );
+    // true while the main thread runs code for the owner; the main thread 
then counts as owner
+    bool                                        isRunningForOwner() const;
+    // runs a function handed over by runOnMainThread(), if any; main thread 
only
+    void                                        runPendingOnMainThread();
 };
 
 #define YIELD_GUARD vos::OGuard aGuard( 
GetSalData()->mpFirstInstance->GetYieldMutex() )
@@ -157,6 +188,11 @@ public:
 
     void wakeupYield();
 
+    template< typename R > static void assignResult( R* pResult, const 
boost::function0<R>& rFunc )
+    {
+        *pResult = rFunc();
+    }
+
  public:
     friend class AquaSalFrame;
 
@@ -165,6 +201,16 @@ public:
 
     bool isNSAppThread() const;
 
+    // AppKit raises or deadlocks when used off the main thread (e.g. by 
remote UNO calls):
+    // off the main thread these run rFunc there and return true, on it they 
return false
+    bool forwardToMainThread( const boost::function0<void>& rFunc );
+    template< typename R > R callOnMainThread( const boost::function0<R>& 
rFunc )
+    {
+        R aResult = R();
+        mpSalYieldMutex->runOnMainThread( boost::bind( 
&AquaSalInstance::assignResult<R>, &aResult, boost::cref( rFunc ) ) );
+        return aResult;
+    }
+
     void startedPrintJob() { mnActivePrintJobs++; }
     void endedPrintJob() { mnActivePrintJobs--; }
 

Reply via email to