diff options
Diffstat (limited to 'vcl/headless')
-rw-r--r-- | vcl/headless/headlessinst.cxx | 2 | ||||
-rw-r--r-- | vcl/headless/svpdata.cxx | 2 | ||||
-rw-r--r-- | vcl/headless/svpinst.cxx | 282 |
3 files changed, 214 insertions, 72 deletions
diff --git a/vcl/headless/headlessinst.cxx b/vcl/headless/headlessinst.cxx index a5f1c6ebd8ea..b49859c03c20 100644 --- a/vcl/headless/headlessinst.cxx +++ b/vcl/headless/headlessinst.cxx @@ -90,7 +90,7 @@ SalData::~SalData() // This is our main entry point: SalInstance *CreateSalInstance() { - HeadlessSalInstance* pInstance = new HeadlessSalInstance( new SalYieldMutex() ); + HeadlessSalInstance* pInstance = new HeadlessSalInstance( new SvpSalYieldMutex() ); new HeadlessSalData( pInstance ); pInstance->AcquireYieldMutex(); return pInstance; diff --git a/vcl/headless/svpdata.cxx b/vcl/headless/svpdata.cxx index 3bc2807c8cc5..ce72305f85d9 100644 --- a/vcl/headless/svpdata.cxx +++ b/vcl/headless/svpdata.cxx @@ -21,7 +21,7 @@ public: // plugin factory function SalInstance* svp_create_SalInstance() { - SvpSalInstance* pInstance = new SvpSalInstance( new SalYieldMutex() ); + SvpSalInstance* pInstance = new SvpSalInstance( new SvpSalYieldMutex() ); new SvpSalData( pInstance ); return pInstance; } diff --git a/vcl/headless/svpinst.cxx b/vcl/headless/svpinst.cxx index 943b824ab412..6d867fcf28a1 100644 --- a/vcl/headless/svpinst.cxx +++ b/vcl/headless/svpinst.cxx @@ -62,15 +62,15 @@ static void atfork_child() #endif -SvpSalInstance::SvpSalInstance( SalYieldMutex *pMutex ) : - SalGenericInstance( pMutex ) +SvpSalInstance::SvpSalInstance( SalYieldMutex *pMutex ) + : SalGenericInstance( pMutex ) { m_aTimeout.tv_sec = 0; m_aTimeout.tv_usec = 0; m_nTimeoutMS = 0; #ifndef IOS - m_pTimeoutFDS[0] = m_pTimeoutFDS[1] = -1; + m_MainThread = osl::Thread::getCurrentIdentifier(); CreateWakeupPipe(true); #endif if( s_pDefaultInstance == nullptr ) @@ -93,60 +93,56 @@ SvpSalInstance::~SvpSalInstance() void SvpSalInstance::CloseWakeupPipe(bool log) { - if (m_pTimeoutFDS[0] != -1) + SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get())); + if (!pMutex) + return; + if (pMutex->m_FeedbackFDs[0] != -1) { if (log) { - SAL_INFO("vcl.headless", "CloseWakeupPipe: Closing inherited wakeup pipe: [" << m_pTimeoutFDS[0] << "," << m_pTimeoutFDS[1] << "]"); + SAL_INFO("vcl.headless", "CloseWakeupPipe: Closing inherited feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]"); } - close (m_pTimeoutFDS[0]); - close (m_pTimeoutFDS[1]); - m_pTimeoutFDS[0] = m_pTimeoutFDS[1] = -1; + close (pMutex->m_FeedbackFDs[0]); + close (pMutex->m_FeedbackFDs[1]); + pMutex->m_FeedbackFDs[0] = pMutex->m_FeedbackFDs[1] = -1; } } void SvpSalInstance::CreateWakeupPipe(bool log) { - if (pipe (m_pTimeoutFDS) == -1) + SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get())); + if (!pMutex) + return; + if (pipe (pMutex->m_FeedbackFDs) == -1) { if (log) { - SAL_WARN("vcl.headless", "Could not create wakeup pipe: " << strerror(errno)); + SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno)); + std::abort(); } } else { if (log) { - SAL_INFO("vcl.headless", "CreateWakeupPipe: Created wakeup pipe: [" << m_pTimeoutFDS[0] << "," << m_pTimeoutFDS[1] << "]"); + SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]"); } - // initialize 'wakeup' pipe. int flags; // set close-on-exec descriptor flag. - if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFD)) != -1) + if ((flags = fcntl (pMutex->m_FeedbackFDs[0], F_GETFD)) != -1) { flags |= FD_CLOEXEC; - (void)fcntl(m_pTimeoutFDS[0], F_SETFD, flags); + (void) fcntl(pMutex->m_FeedbackFDs[0], F_SETFD, flags); } - if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFD)) != -1) + if ((flags = fcntl (pMutex->m_FeedbackFDs[1], F_GETFD)) != -1) { flags |= FD_CLOEXEC; - (void)fcntl(m_pTimeoutFDS[1], F_SETFD, flags); + (void) fcntl(pMutex->m_FeedbackFDs[1], F_SETFD, flags); } - // set non-blocking I/O flag. - if ((flags = fcntl(m_pTimeoutFDS[0], F_GETFL)) != -1) - { - flags |= O_NONBLOCK; - (void)fcntl(m_pTimeoutFDS[0], F_SETFL, flags); - } - if ((flags = fcntl(m_pTimeoutFDS[1], F_GETFL)) != -1) - { - flags |= O_NONBLOCK; - (void)fcntl(m_pTimeoutFDS[1], F_SETFL, flags); - } + // retain the default blocking I/O for feedback pipe } } @@ -157,10 +153,29 @@ void SvpSalInstance::TriggerUserEventProcessing() Wakeup(); } -void SvpSalInstance::Wakeup() +#ifndef NDEBUG +static bool g_CheckedMutex = false; +#endif + +void SvpSalInstance::Wakeup(SvpRequest const request) { +#ifndef NDEBUG + if (!g_CheckedMutex) + { + assert(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()) != nullptr + && "This SvpSalInstance function requires use of SvpSalYieldMutex"); + g_CheckedMutex = true; + } +#endif #ifndef IOS - OSL_VERIFY(write (m_pTimeoutFDS[1], "", 1) == 1); + SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get())); + std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex); + if (request != SvpRequest::NONE) + { + pMutex->m_Request = request; + } + pMutex->m_wakeUpMain = true; + pMutex->m_WakeUpMainCond.notify_one(); #endif } @@ -259,72 +274,199 @@ void SvpSalInstance::ProcessEvent( SalUserEvent aEvent ) const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame); pSvpFrame->PostPaint(); } +#ifndef NDEBUG + if (!g_CheckedMutex) + { + assert(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()) != nullptr + && "This SvpSalInstance function requires use of SvpSalYieldMutex"); + g_CheckedMutex = true; + } +#endif + SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get())); + pMutex->m_NonMainWaitingYieldCond.set(); } - -bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents) +SvpSalYieldMutex::SvpSalYieldMutex() { - // first, process current user events - bool bEvent = DispatchUserEvents( bHandleAllCurrentEvents ); - if ( !bHandleAllCurrentEvents && bEvent ) - return true; + m_FeedbackFDs[0] = m_FeedbackFDs[1] = -1; +} - bEvent = CheckTimeout() || bEvent; +SvpSalYieldMutex::~SvpSalYieldMutex() +{ +} - if (bWait && ! bEvent ) +void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount) +{ + SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance); + if (pInst && pInst->IsMainThread()) { - int nTimeoutMS = 0; - if (m_aTimeout.tv_sec) // Timer is started. + if (m_bNoYieldLock) + return; + + do { - timeval Timeout; - // determine remaining timeout. - gettimeofday (&Timeout, nullptr); - if ( m_aTimeout > Timeout ) + SvpRequest request = SvpRequest::NONE; + { + std::unique_lock<std::mutex> g(m_WakeUpMainMutex); + if (m_aMutex.tryToAcquire()) { + // if there's a request, the other thread holds m_aMutex + assert(m_Request == SvpRequest::NONE); + m_wakeUpMain = false; + break; + } + m_WakeUpMainCond.wait(g, [this]() { return m_wakeUpMain; }); + m_wakeUpMain = false; + std::swap(m_Request, request); + } + if (request != SvpRequest::NONE) { - int nTimeoutMicroS = m_aTimeout.tv_usec - Timeout.tv_usec; - nTimeoutMS = (m_aTimeout.tv_sec - Timeout.tv_sec) * 1000 - + nTimeoutMicroS / 1000; - if ( nTimeoutMicroS % 1000 ) - nTimeoutMS += 1; + // nested Yield on behalf of another thread + assert(!m_bNoYieldLock); + m_bNoYieldLock = true; + bool const bEvents = pInst->DoYield(false, request == SvpRequest::MainThreadDispatchAllEvents); + m_bNoYieldLock = false; + write(m_FeedbackFDs[1], &bEvents, sizeof(bool)); } } + while (true); + } + else + { + m_aMutex.acquire(); + } + ++m_nCount; + SalYieldMutex::doAcquire(nLockCount - 1); +} + +sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll) +{ + SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance); + if (pInst && pInst->IsMainThread()) + { + if (m_bNoYieldLock) + return 1; else - nTimeoutMS = -1; // wait until something happens + return SalYieldMutex::doRelease(bUnlockAll); + } + sal_uInt32 nCount; + { + // read m_nCount before doRelease + bool const isReleased(bUnlockAll || m_nCount == 1); + nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll ); + if (isReleased) { + std::unique_lock<std::mutex> g(m_WakeUpMainMutex); + m_wakeUpMain = true; + m_WakeUpMainCond.notify_one(); + } + } + return nCount; +} - DoReleaseYield(nTimeoutMS); +bool SvpSalYieldMutex::IsCurrentThread() const +{ + if (GetSalData()->m_pInstance->IsMainThread() && m_bNoYieldLock) + { + return true; } - else if ( bEvent ) + else { - // Drain the wakeup pipe - int buffer; - while (read (m_pTimeoutFDS[0], &buffer, sizeof(buffer)) > 0); + return SalYieldMutex::IsCurrentThread(); } +} - return bEvent; +bool SvpSalInstance::IsMainThread() const +{ + return osl::Thread::getCurrentIdentifier() == m_MainThread; } -void SvpSalInstance::DoReleaseYield( int nTimeoutMS ) +bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents) { - // poll - struct pollfd aPoll; - aPoll.fd = m_pTimeoutFDS[0]; - aPoll.events = POLLIN; - aPoll.revents = 0; +#ifndef NDEBUG + if (!g_CheckedMutex) + { + assert(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()) != nullptr + && "This SvpSalInstance function requires use of SvpSalYieldMutex"); + g_CheckedMutex = true; + } +#endif - // release yield mutex - sal_uInt32 nAcquireCount = ReleaseYieldMutexAll(); + // first, process current user events + bool bEvent = DispatchUserEvents( bHandleAllCurrentEvents ); + if ( !bHandleAllCurrentEvents && bEvent ) + return true; - (void)poll( &aPoll, 1, nTimeoutMS ); + bEvent = CheckTimeout() || bEvent; - // acquire yield mutex again - AcquireYieldMutex( nAcquireCount ); + SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get())); - // clean up pipe - if( (aPoll.revents & POLLIN) != 0 ) + if (IsMainThread()) + { + if (bWait && ! bEvent) + { + int nTimeoutMS = 0; + if (m_aTimeout.tv_sec) // Timer is started. + { + timeval Timeout; + // determine remaining timeout. + gettimeofday (&Timeout, nullptr); + if (m_aTimeout > Timeout) + { + int nTimeoutMicroS = m_aTimeout.tv_usec - Timeout.tv_usec; + nTimeoutMS = (m_aTimeout.tv_sec - Timeout.tv_sec) * 1000 + + nTimeoutMicroS / 1000; + if ( nTimeoutMicroS % 1000 ) + nTimeoutMS += 1; + } + } + else + nTimeoutMS = -1; // wait until something happens + + sal_uInt32 nAcquireCount = ReleaseYieldMutexAll(); + { + std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex); + // wait for doRelease() or Wakeup() to set the condition + if (nTimeoutMS == -1) + { + pMutex->m_WakeUpMainCond.wait(g, + [pMutex]() { return pMutex->m_wakeUpMain; }); + } + else + { + pMutex->m_WakeUpMainCond.wait_for(g, + std::chrono::milliseconds(nTimeoutMS), + [pMutex]() { return pMutex->m_wakeUpMain; }); + } + // here no need to check m_Request because Acquire will do it + } + AcquireYieldMutex( nAcquireCount ); + } + else if (bEvent) + { + pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads + } + } + else // !IsMainThread() { - int buffer; - while (read (m_pTimeoutFDS[0], &buffer, sizeof(buffer)) > 0); + Wakeup(bHandleAllCurrentEvents + ? SvpRequest::MainThreadDispatchAllEvents + : SvpRequest::MainThreadDispatchOneEvent); + + bool bDidWork(false); + // blocking read (for synchronisation) + auto const nRet = read(pMutex->m_FeedbackFDs[0], &bDidWork, sizeof(bool)); + assert(nRet == 1); (void) nRet; + + if (!bDidWork && bWait) + { + // block & release YieldMutex until the main thread does something + pMutex->m_NonMainWaitingYieldCond.reset(); + sal_uInt32 nAcquireCount = ReleaseYieldMutexAll(); + pMutex->m_NonMainWaitingYieldCond.wait(); + AcquireYieldMutex( nAcquireCount ); + } } + + return bEvent; } bool SvpSalInstance::AnyInput( VclInputFlags nType ) |