diff options
author | Jan-Marek Glogowski <glogow@fbihome.de> | 2017-08-08 15:03:37 +0200 |
---|---|---|
committer | Jan-Marek Glogowski <glogow@fbihome.de> | 2017-09-21 09:04:29 +0200 |
commit | 9796f792fef69bbe01b674365643d1fbb79918b4 (patch) | |
tree | bc684f43077f5b70ec5553d4f5253ca57f0b023b | |
parent | 4067487eb304c6686a22319c51790e41e311de08 (diff) |
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
Reviewed-on: https://gerrit.libreoffice.org/42448
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
-rw-r--r-- | include/vcl/pointr.hxx | 2 | ||||
-rw-r--r-- | vcl/README.scheduler | 26 | ||||
-rw-r--r-- | vcl/inc/osx/runinmain.hxx | 150 | ||||
-rw-r--r-- | vcl/inc/osx/salframe.h | 8 | ||||
-rw-r--r-- | vcl/inc/osx/salinst.h | 21 | ||||
-rw-r--r-- | vcl/osx/a11ytextwrapper.mm | 10 | ||||
-rw-r--r-- | vcl/osx/salframe.cxx | 182 | ||||
-rw-r--r-- | vcl/osx/salinst.cxx | 139 | ||||
-rw-r--r-- | vcl/osx/salobj.cxx | 27 |
9 files changed, 413 insertions, 152 deletions
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..271ce1949695 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. Those wakeup events must be ignored to prevent busy-locks. + +We can neither 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,15 @@ 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. + +Instead of LO running in the main process / thread, we run it in a 2nd thread +and defer al GUI calls to the main thread. This way it'll hopefully not block +and can process system events. + +That's just a theory - it definitly needs more analysis before even attemding +an implementation. 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/salframe.h b/vcl/inc/osx/salframe.h index 4b6d486f6be6..e99a3175876d 100644 --- a/vcl/inc/osx/salframe.h +++ b/vcl/inc/osx/salframe.h @@ -183,22 +183,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 f1598a4f1040..1f456f737901 100644 --- a/vcl/inc/osx/salinst.h +++ b/vcl/inc/osx/salinst.h @@ -32,16 +32,31 @@ #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; + +public: SalYieldMutex(); virtual ~SalYieldMutex() override; + + virtual bool IsCurrentThread() const override; }; class AquaSalInstance : public SalInstance @@ -61,12 +76,12 @@ public: SalYieldMutex* mpSalYieldMutex; // Sal-Yield-Mutex OUString maDefaultPrinter; oslThreadIdentifier maMainThread; - bool mbWaitingYield; int mnActivePrintJobs; std::list< SalUserEvent > maUserEvents; osl::Mutex maUserEventListMutex; osl::Condition maWaitingYieldCond; bool mbIsLiveResize; + bool mbNoYieldLock; static std::list<const ApplicationEvent*> aAppEventList; @@ -126,9 +141,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 ); 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; sal_Int32 index = [ wrapper accessibleText ] -> getIndexAtPoint( aPoint ); @@ -239,9 +239,9 @@ using namespace ::com::sun::star::uno; } if ( [ wrapper accessibleComponent ] ) { // get location on screen (must be added since get CharacterBounds returns values relative to parent) - Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen(); - Point pos ( minx + screenPos.X, miny + screenPos.Y ); - Point size ( maxx - minx, maxy - miny ); + css::awt::Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen(); + css::awt::Point pos ( minx + screenPos.X, miny + screenPos.Y ); + css::awt::Point size ( maxx - minx, maxy - miny ); NSValue * nsPos = [ AquaA11yUtil vclPointToNSPoint: pos ]; rect = [ NSValue valueWithRect: NSMakeRect ( [ nsPos pointValue ].x, [ nsPos pointValue ].y - size.Y, size.X, size.Y ) ]; //printf("Range: %s --- Rect: %s\n", [ NSStringFromRange ( [ range rangeValue ] ) UTF8String ], [ NSStringFromRect ( [ rect rectValue ] ) UTF8String ]); diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx index a29eba5eb766..e54073cdf4f1 100644 --- a/vcl/osx/salframe.cxx +++ b/vcl/osx/salframe.cxx @@ -36,6 +36,7 @@ #include "osx/salinst.h" #include "osx/salframeview.h" #include "osx/a11yfactory.h" +#include "osx/runinmain.hxx" #include "quartz/utils.h" #include "salwtype.hxx" @@ -92,6 +93,8 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, SalFrameStyleFlags salFrameStyle AquaSalFrame::~AquaSalFrame() { + assert( GetSalData()->mpFirstInstance->IsMainThread() ); + // if the frame is destroyed and has the current menubar // set the default menubar if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu ) @@ -134,6 +137,8 @@ AquaSalFrame::~AquaSalFrame() void AquaSalFrame::initWindowAndView() { + OSX_SALDATA_RUNINMAIN( initWindowAndView() ) + // initialize mirroring parameters // FIXME: screens changing NSScreen* pNSScreen = [mpNSWindow screen]; @@ -259,6 +264,8 @@ void AquaSalFrame::VCLToCocoa( NSPoint& io_rPoint, bool bRelativeToScreen ) void AquaSalFrame::screenParametersChanged() { + OSX_SALDATA_RUNINMAIN( screenParametersChanged() ) + UpdateFrameGeometry(); if( mpGraphics ) @@ -298,6 +305,8 @@ void AquaSalFrame::SetTitle(const OUString& rTitle) if ( !mpNSWindow ) return; + OSX_SALDATA_RUNINMAIN( SetTitle(rTitle) ) + // #i113170# may not be the main thread if called from UNO API SalData::ensureThreadAutoreleasePool(); @@ -342,8 +351,7 @@ void AquaSalFrame::SetIcon( sal_uInt16 ) void AquaSalFrame::SetRepresentedURL( const OUString& i_rDocURL ) { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetRepresentedURL( i_rDocURL ) ) if( comphelper::isFileUrl(i_rDocURL) ) { @@ -360,6 +368,8 @@ void AquaSalFrame::SetRepresentedURL( const OUString& i_rDocURL ) void AquaSalFrame::initShow() { + OSX_SALDATA_RUNINMAIN( initShow() ) + mbInitShow = false; if( ! mbPositioned && ! mbFullScreen ) { @@ -397,6 +407,8 @@ void AquaSalFrame::initShow() void AquaSalFrame::SendPaintEvent( const tools::Rectangle* pRect ) { + OSX_SALDATA_RUNINMAIN( SendPaintEvent( pRect ) ) + SalPaintEvent aPaintEvt( 0, 0, maGeometry.nWidth, maGeometry.nHeight, true ); if( pRect ) { @@ -414,8 +426,7 @@ void AquaSalFrame::Show(bool bVisible, bool bNoActivate) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( Show(bVisible, bNoActivate) ) mbShown = bVisible; if(bVisible) @@ -472,8 +483,7 @@ void AquaSalFrame::Show(bool bVisible, bool bNoActivate) void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight ) { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetMinClientSize( nWidth, nHeight ) ) mnMinWidth = nWidth; mnMinHeight = nHeight; @@ -495,8 +505,7 @@ void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight ) void AquaSalFrame::SetMaxClientSize( long nWidth, long nHeight ) { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetMaxClientSize( nWidth, nHeight ) ) mnMaxWidth = nWidth; mnMaxHeight = nHeight; @@ -536,46 +545,45 @@ void AquaSalFrame::GetClientSize( long& rWidth, long& rHeight ) void AquaSalFrame::SetWindowState( const SalFrameState* pState ) { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetWindowState( pState ) ) if ( mpNSWindow ) { - // set normal state - NSRect aStateRect = [mpNSWindow frame]; - aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask]; - CocoaToVCL( aStateRect ); - if( pState->mnMask & WindowStateMask::X ) - aStateRect.origin.x = float(pState->mnX); - if( pState->mnMask & WindowStateMask::Y ) - aStateRect.origin.y = float(pState->mnY); - if( pState->mnMask & WindowStateMask::Width ) - aStateRect.size.width = float(pState->mnWidth); - if( pState->mnMask & WindowStateMask::Height ) - aStateRect.size.height = float(pState->mnHeight); - VCLToCocoa( aStateRect ); - aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: mnStyleMask]; - - [mpNSWindow setFrame: aStateRect display: NO]; - if( pState->mnState == WindowStateState::Minimized ) - [mpNSWindow miniaturize: NSApp]; - else if( [mpNSWindow isMiniaturized] ) - [mpNSWindow deminiaturize: NSApp]; - - /* ZOOMED is not really maximized (actually it toggles between a user set size and - the program specified one), but comes closest since the default behavior is - "maximized" if the user did not intervene - */ - if( pState->mnState == WindowStateState::Maximized ) - { - if(! [mpNSWindow isZoomed]) - [mpNSWindow zoom: NSApp]; - } - else - { - if( [mpNSWindow isZoomed] ) - [mpNSWindow zoom: NSApp]; - } + // set normal state + NSRect aStateRect = [mpNSWindow frame]; + aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask]; + CocoaToVCL( aStateRect ); + if( pState->mnMask & WindowStateMask::X ) + aStateRect.origin.x = float(pState->mnX); + if( pState->mnMask & WindowStateMask::Y ) + aStateRect.origin.y = float(pState->mnY); + if( pState->mnMask & WindowStateMask::Width ) + aStateRect.size.width = float(pState->mnWidth); + if( pState->mnMask & WindowStateMask::Height ) + aStateRect.size.height = float(pState->mnHeight); + VCLToCocoa( aStateRect ); + aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: mnStyleMask]; + + [mpNSWindow setFrame: aStateRect display: NO]; + if( pState->mnState == WindowStateState::Minimized ) + [mpNSWindow miniaturize: NSApp]; + else if( [mpNSWindow isMiniaturized] ) + [mpNSWindow deminiaturize: NSApp]; + + /* ZOOMED is not really maximized (actually it toggles between a user set size and + the program specified one), but comes closest since the default behavior is + "maximized" if the user did not intervene + */ + if( pState->mnState == WindowStateState::Maximized ) + { + if(! [mpNSWindow isZoomed]) + [mpNSWindow zoom: NSApp]; + } + else + { + if( [mpNSWindow isZoomed] ) + [mpNSWindow zoom: NSApp]; + } } // get new geometry @@ -612,8 +620,7 @@ bool AquaSalFrame::GetWindowState( SalFrameState* pState ) if ( !mpNSWindow ) return FALSE; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN_UNION( GetWindowState( pState ), boolean ) pState->mnMask = WindowStateMask::X | WindowStateMask::Y | @@ -644,8 +651,7 @@ void AquaSalFrame::SetScreenNumber(unsigned int nScreen) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetScreenNumber( nScreen ) ) NSArray* pScreens = [NSScreen screens]; NSScreen* pScreen = nil; @@ -682,14 +688,13 @@ void AquaSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nDisplay ) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); - SAL_INFO("vcl.osx", OSL_THIS_FUNC << ": mbFullScreen=" << mbFullScreen << ", bFullScreen=" << bFullScreen); if( mbFullScreen == bFullScreen ) return; + OSX_SALDATA_RUNINMAIN( ShowFullScreen( bFullScreen, nDisplay ) ) + mbFullScreen = bFullScreen; if( bFullScreen ) @@ -777,8 +782,7 @@ void AquaSalFrame::StartPresentation( bool bStart ) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( StartPresentation( bStart ) ) if( bStart ) { @@ -808,8 +812,7 @@ void AquaSalFrame::ToTop(SalFrameToTop nFlags) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( ToTop( nFlags ) ) if( ! (nFlags & SalFrameToTop::RestoreWhenMin) ) { @@ -822,8 +825,10 @@ void AquaSalFrame::ToTop(SalFrameToTop nFlags) [mpNSWindow orderFront: NSApp]; } -NSCursor* AquaSalFrame::getCurrentCursor() const +NSCursor* AquaSalFrame::getCurrentCursor() { + OSX_SALDATA_RUNINMAIN_POINTER( getCurrentCursor(), NSCursor* ) + NSCursor* pCursor = nil; switch( mePointerStyle ) { @@ -864,12 +869,11 @@ void AquaSalFrame::SetPointer( PointerStyle ePointerStyle ) { if ( !mpNSWindow ) return; - - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); - if( ePointerStyle == mePointerStyle ) return; + + OSX_SALDATA_RUNINMAIN( SetPointer( ePointerStyle ) ) + mePointerStyle = ePointerStyle; [mpNSWindow invalidateCursorRectsForView: mpNSView]; @@ -877,8 +881,9 @@ void AquaSalFrame::SetPointer( PointerStyle ePointerStyle ) void AquaSalFrame::SetPointerPos( long nX, long nY ) { - // FIXME: use Cocoa functions + OSX_SALDATA_RUNINMAIN( SetPointerPos( nX, nY ) ) + // FIXME: use Cocoa functions // FIXME: multiscreen support CGPoint aPoint = { static_cast<CGFloat>(nX + maGeometry.nX), static_cast<CGFloat>(nY + maGeometry.nY) }; CGDirectDisplayID mainDisplayID = CGMainDisplayID(); @@ -890,8 +895,7 @@ void AquaSalFrame::Flush() if( !(mbGraphics && mpGraphics && mpNSView && mbShown) ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( Flush() ) [mpNSView setNeedsDisplay: YES]; @@ -909,8 +913,7 @@ void AquaSalFrame::Flush( const tools::Rectangle& rRect ) if( !(mbGraphics && mpGraphics && mpNSView && mbShown) ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( Flush( rRect ) ) NSRect aNSRect = { { static_cast<CGFloat>(rRect.Left()), static_cast<CGFloat>(rRect.Top()) }, { static_cast<CGFloat>(rRect.GetWidth()), static_cast<CGFloat>(rRect.GetHeight()) } }; VCLToCocoa( aNSRect, false ); @@ -1118,6 +1121,8 @@ static vcl::Font getFont( NSFont* pFont, long nDPIY, const vcl::Font& rDefault ) void AquaSalFrame::getResolution( sal_Int32& o_rDPIX, sal_Int32& o_rDPIY ) { + OSX_SALDATA_RUNINMAIN( getResolution( o_rDPIX, o_rDPIY ) ) + if( ! mpGraphics ) { AcquireGraphics(); @@ -1137,8 +1142,7 @@ void AquaSalFrame::UpdateSettings( AllSettings& rSettings ) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( UpdateSettings( rSettings ) ) [mpNSView lockFocus]; @@ -1242,6 +1246,7 @@ const SystemEnvData* AquaSalFrame::GetSystemData() const void AquaSalFrame::Beep() { + OSX_SALDATA_RUNINMAIN( Beep() ) NSBeep(); } @@ -1250,8 +1255,7 @@ void AquaSalFrame::SetPosSize(long nX, long nY, long nWidth, long nHeight, sal_u if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetPosSize( nX, nY, nWidth, nHeight, nFlags ) ) SalEvent nEvent = SalEvent::NONE; @@ -1340,8 +1344,7 @@ void AquaSalFrame::GetWorkArea( tools::Rectangle& rRect ) if ( !mpNSWindow ) return; - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( GetWorkArea( rRect ) ) NSScreen* pScreen = [mpNSWindow screen]; if( pScreen == nil ) @@ -1356,8 +1359,7 @@ void AquaSalFrame::GetWorkArea( tools::Rectangle& rRect ) SalPointerState AquaSalFrame::GetPointerState() { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN_UNION( GetPointerState(), state ) SalPointerState state; state.mnState = 0; @@ -1492,8 +1494,7 @@ void AquaSalFrame::DrawMenuBar() void AquaSalFrame::SetMenu( SalMenu* pSalMenu ) { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( SetMenu( pSalMenu ) ) AquaSalMenu* pMenu = static_cast<AquaSalMenu*>(pSalMenu); SAL_WARN_IF( pMenu && !pMenu->mbMenuBar, "vcl", "setting non menubar on frame" ); @@ -1504,14 +1505,16 @@ void AquaSalFrame::SetMenu( SalMenu* pSalMenu ) void AquaSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) { - if ( mpNSWindow ) + if ( !mpNSWindow ) { - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + mnExtStyle = nStyle; + return; + } + + OSX_SALDATA_RUNINMAIN( SetExtendedFrameStyle( nStyle ) ) if( (mnExtStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) != (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) ) [mpNSWindow setDocumentEdited: (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) ? YES : NO]; - } mnExtStyle = nStyle; } @@ -1534,9 +1537,9 @@ void AquaSalFrame::SetParent( SalFrame* pNewParent ) void AquaSalFrame::UpdateFrameGeometry() { if ( !mpNSWindow ) - { return; - } + + OSX_SALDATA_RUNINMAIN( UpdateFrameGeometry() ) // keep in mind that view and window coordinates are lower left // whereas vcl's are upper left @@ -1609,12 +1612,9 @@ void AquaSalFrame::CaptureMouse( bool bCapture ) void AquaSalFrame::ResetClipRegion() { if ( !mpNSWindow ) - { return; - } - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( ResetClipRegion() ) // release old path and indicate no clipping CGPathRelease( mrClippingPath ); @@ -1632,12 +1632,9 @@ void AquaSalFrame::ResetClipRegion() void AquaSalFrame::BeginSetClipRegion( sal_uLong nRects ) { if ( !mpNSWindow ) - { return; - } - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( BeginSetClipRegion( nRects ) ) // release old path if( mrClippingPath ) @@ -1671,12 +1668,9 @@ void AquaSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight void AquaSalFrame::EndSetClipRegion() { if ( !mpNSWindow ) - { return; - } - // #i113170# may not be the main thread if called from UNO API - SalData::ensureThreadAutoreleasePool(); + OSX_SALDATA_RUNINMAIN( EndSetClipRegion() ) if( ! maClippingRects.empty() ) { diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx index 56dd7bef8feb..d33c0a33af25 100644 --- a/vcl/osx/salinst.cxx +++ b/vcl/osx/salinst.cxx @@ -46,6 +46,7 @@ #include "osx/salprn.h" #include "osx/saltimer.h" #include "osx/vclnsapp.h" +#include "osx/runinmain.hxx" #include "print.h" #include "impbmp.hxx" @@ -259,6 +260,7 @@ void InitSalMain() } SalYieldMutex::SalYieldMutex() + : m_aCodeBlock( nullptr ) { } @@ -266,6 +268,63 @@ SalYieldMutex::~SalYieldMutex() { } +void SalYieldMutex::doAcquire( sal_uInt32 nLockCount ) +{ + AquaSalInstance *pInst = GetSalData()->mpFirstInstance; + if ( pInst && pInst->IsMainThread() ) + { + if ( pInst->mbNoYieldLock ) + return; + do { + m_aInMainCondition.reset(); + if ( m_aCodeBlock ) + { + assert( !pInst->mbNoYieldLock ); + pInst->mbNoYieldLock = true; + m_aCodeBlock(); + pInst->mbNoYieldLock = false; + Block_release( m_aCodeBlock ); + m_aCodeBlock = nullptr; + m_aResultCondition.set(); + } + // reset condition *before* acquiring! + if (m_aMutex.tryToAcquire()) + break; + // wait for doRelease() or RUNINMAIN_* to set the condition + osl::Condition::Result res = m_aInMainCondition.wait(); + assert(osl::Condition::Result::result_ok == res); + } + while ( true ); + } + else + m_aMutex.acquire(); + ++m_nCount; + --nLockCount; + + comphelper::GenericSolarMutex::doAcquire( nLockCount ); +} + +sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll ) +{ + AquaSalInstance *pInst = GetSalData()->mpFirstInstance; + if ( pInst->mbNoYieldLock && pInst->IsMainThread() ) + return 1; + sal_uInt32 nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll ); + + if ( 0 == m_nCount && !pInst->IsMainThread() ) + m_aInMainCondition.set(); + + return nCount; +} + +bool SalYieldMutex::IsCurrentThread() const +{ + if ( !GetSalData()->mpFirstInstance->mbNoYieldLock ) + return comphelper::GenericSolarMutex::IsCurrentThread(); + else + return GetSalData()->mpFirstInstance->IsMainThread(); +} + // some convenience functions regarding the yield mutex, aka solar mutex bool ImplSalYieldMutexTryToAcquire() @@ -315,15 +374,13 @@ void DestroySalInstance( SalInstance* pInst ) } AquaSalInstance::AquaSalInstance() - : maUserEventListMutex() - , maWaitingYieldCond() - , mbIsLiveResize( false ) + : mnActivePrintJobs( 0 ) + , mbIsLiveResize( false ) + , mbNoYieldLock( false ) { mpSalYieldMutex = new SalYieldMutex; mpSalYieldMutex->acquire(); maMainThread = osl::Thread::getCurrentIdentifier(); - mbWaitingYield = false; - mnActivePrintJobs = 0; } AquaSalInstance::~AquaSalInstance() @@ -332,21 +389,15 @@ 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(); + dispatch_async(dispatch_get_main_queue(),^{ + ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO ); + }); } comphelper::SolarMutex* AquaSalInstance::GetYieldMutex() @@ -465,7 +516,7 @@ void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) case YieldWakeupEvent: // do nothing, fall out of Yield - break; + break; default: OSL_FAIL( "unhandled NSApplicationDefined event" ); @@ -473,6 +524,14 @@ void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) }; } +static bool isWakeupEvent( NSEvent *pEvent ) +{ +SAL_WNODEPRECATED_DECLARATIONS_PUSH + return NSApplicationDefined == [pEvent type] + && AquaSalInstance::YieldWakeupEvent == (int) [pEvent subtype]; +SAL_WNODEPRECATED_DECLARATIONS_POP +} + bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased) { (void) nReleased; @@ -522,11 +581,6 @@ bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLon // cocoa events may be only handled in the thread the NSApp was created if( IsMainThread() && mnActivePrintJobs == 0 ) { - // we need to be woken up by a cocoa-event - // if a user event should be posted by the event handling below - bool bOldWaitingYield = mbWaitingYield; - mbWaitingYield = bWait; - // handle available events NSEvent* pEvent = nil; do @@ -543,12 +597,17 @@ SAL_WNODEPRECATED_DECLARATIONS_POP if( pEvent ) { [NSApp sendEvent: pEvent]; + if ( isWakeupEvent( pEvent ) ) + continue; bHadEvent = true; } [NSApp updateWindows]; + + if ( !bHandleAllCurrentEvents || !pEvent ) + break; } - while( bHandleAllCurrentEvents && pEvent ); + while( true ); // if we had no event yet, wait for one if requested if( bWait && ! bHadEvent ) @@ -564,12 +623,14 @@ SAL_WNODEPRECATED_DECLARATIONS_POP inMode: NSDefaultRunLoopMode dequeue: YES]; if( pEvent ) + { [NSApp sendEvent: pEvent]; + if ( !isWakeupEvent( pEvent ) ) + bHadEvent = true; + } [NSApp updateWindows]; } - mbWaitingYield = bOldWaitingYield; - // collect update rectangles const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames ); for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) @@ -580,17 +641,17 @@ SAL_WNODEPRECATED_DECLARATIONS_POP (*it)->maInvalidRect.SetEmpty(); } } - maWaitingYieldCond.set(); + + if ( bHadEvent ) + maWaitingYieldCond.set(); } else if( bWait ) { // #i103162# - // wait until any thread (most likely the main thread) - // has dispatched an event, cop out at 200 ms + // wait until the main thread has dispatched an event maWaitingYieldCond.reset(); - TimeValue aVal = { 0, 200000000 }; SolarMutexReleaser aReleaser; - maWaitingYieldCond.wait( &aVal ); + maWaitingYieldCond.wait(); } // we get some apple events way too early @@ -629,6 +690,8 @@ bool AquaSalInstance::AnyInput( VclInputFlags nType ) return false; } + OSX_INST_RUNINMAIN_UNION( AnyInput( nType ), boolean ) + if( nType & VclInputFlags::TIMER ) { if( AquaSalTimer::pRunningTimer ) @@ -641,9 +704,6 @@ bool AquaSalInstance::AnyInput( VclInputFlags nType ) } } - if (!IsMainThread()) - return false; - unsigned/*NSUInteger*/ nEventMask = 0; SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'NSFlagsChangedMask' is deprecated: first deprecated in macOS 10.12 @@ -691,29 +751,28 @@ SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, SalFrameStyleFla SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle ) { - SalData::ensureThreadAutoreleasePool(); - - SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle ); - return pFrame; + OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent, nSalFrameStyle ), SalFrame* ) + return new AquaSalFrame( pParent, nSalFrameStyle ); } void AquaSalInstance::DestroyFrame( SalFrame* pFrame ) { + OSX_INST_RUNINMAIN( DestroyFrame( pFrame ) ) delete pFrame; } SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool /* bShow */ ) { - AquaSalObject *pObject = nullptr; - - if ( pParent ) - pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData ); + if ( !pParent ) + return nullptr; - return pObject; + OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent, pWindowData, false ), SalObject* ) + return new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData ); } void AquaSalInstance::DestroyObject( SalObject* pObject ) { + OSX_INST_RUNINMAIN( DestroyObject( pObject ) ) delete pObject; } diff --git a/vcl/osx/salobj.cxx b/vcl/osx/salobj.cxx index b8435d3b4368..cdd60120a7ff 100644 --- a/vcl/osx/salobj.cxx +++ b/vcl/osx/salobj.cxx @@ -26,6 +26,7 @@ #include "osx/salframe.h" #include "osx/salinst.h" #include "osx/salobj.h" +#include "osx/runinmain.hxx" #include <AppKit/NSOpenGLView.h> @@ -105,6 +106,8 @@ AquaSalObject::AquaSalObject( AquaSalFrame* pFrame, SystemWindowData const * pWi AquaSalObject::~AquaSalObject() { + assert( GetSalData()->mpFirstInstance->IsMainThread() ); + if( maSysData.mpNSView ) { NSView *pView = maSysData.mpNSView; @@ -194,6 +197,8 @@ void AquaSalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight ) void AquaSalObject::setClippedPosSize() { + OSX_SALDATA_RUNINMAIN( setClippedPosSize() ) + NSRect aViewRect = { NSZeroPoint, NSMakeSize( mnWidth, mnHeight) }; if( maSysData.mpNSView ) { @@ -222,8 +227,12 @@ void AquaSalObject::setClippedPosSize() void AquaSalObject::Show( bool bVisible ) { - if( mpClipView ) - [mpClipView setHidden: (bVisible ? NO : YES)]; + if( !mpClipView ) + return; + + OSX_SALDATA_RUNINMAIN( Show( bVisible ) ) + + [mpClipView setHidden: (bVisible ? NO : YES)]; } const SystemEnvData* AquaSalObject::GetSystemData() const @@ -235,8 +244,10 @@ class AquaOpenGLContext : public OpenGLContext { public: virtual bool initWindow() override; + private: GLWindow m_aGLWin; + virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; } virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; } NSOpenGLView* getOpenGLView(); @@ -250,6 +261,8 @@ private: void AquaOpenGLContext::resetCurrent() { + OSX_SALDATA_RUNINMAIN( resetCurrent() ) + clearCurrent(); OpenGLZone aZone; @@ -260,6 +273,8 @@ void AquaOpenGLContext::resetCurrent() void AquaOpenGLContext::makeCurrent() { + OSX_SALDATA_RUNINMAIN( makeCurrent() ) + if (isCurrent()) return; @@ -275,6 +290,8 @@ void AquaOpenGLContext::makeCurrent() void AquaOpenGLContext::swapBuffers() { + OSX_SALDATA_RUNINMAIN( swapBuffers() ) + OpenGLZone aZone; NSOpenGLView* pView = getOpenGLView(); @@ -293,11 +310,14 @@ SystemWindowData AquaOpenGLContext::generateWinData(vcl::Window* /*pParent*/, bo void AquaOpenGLContext::destroyCurrentContext() { + OSX_SALDATA_RUNINMAIN( destroyCurrentContext() ) [NSOpenGLContext clearCurrentContext]; } bool AquaOpenGLContext::initWindow() { + OSX_SALDATA_RUNINMAIN_UNION( initWindow(), boolean ) + if( !m_pChildWindow ) { SystemWindowData winData = generateWinData(mpWindow, mbRequestLegacyContext); @@ -314,6 +334,8 @@ bool AquaOpenGLContext::initWindow() bool AquaOpenGLContext::ImplInit() { + OSX_SALDATA_RUNINMAIN_UNION( ImplInit(), boolean ) + OpenGLZone aZone; VCL_GL_INFO("OpenGLContext::ImplInit----start"); @@ -332,6 +354,7 @@ NSOpenGLView* AquaOpenGLContext::getOpenGLView() OpenGLContext* AquaSalInstance::CreateOpenGLContext() { + OSX_SALDATA_RUNINMAIN_POINTER( CreateOpenGLContext(), OpenGLContext* ) return new AquaOpenGLContext; } |