Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/headless/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 16 kB image not shown  

Quelle  svpinst.cxx   Sprache: C

 
/* -*- 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 .
 */


#include <sal/config.h>

#include <mutex>

#include <pthread.h>
#include <sys/time.h>

#include <sal/types.h>

#include <vcl/virdev.hxx>
#include <vcl/inputtypes.hxx>
#include <vcl/lok.hxx>

#include <headless/svpinst.hxx>
#include <headless/svpframe.hxx>
#include <headless/svpdummies.hxx>
#include <headless/svpvd.hxx>
#ifdef IOS
#  include <quartz/salbmp.h>
#  include <quartz/salgdi.h>
#  include <quartz/salvd.h>
#else
#  include <cairo.h>
#  include <headless/svpgdi.hxx>
#endif
#include <headless/svpbmp.hxx>

#include <salframe.hxx>
#include <svdata.hxx>
// FIXME: remove when we re-work the svp mainloop
#include <unx/salunxtime.h>
#include <tools/debug.hxx>
#include <comphelper/lok.hxx>
#include <o3tl/unreachable.hxx>

#if defined EMSCRIPTEN
#include <emscripten.h>
#endif

SvpSalInstance* SvpSalInstance::s_pDefaultInstance = nullptr;

#ifndef NDEBUG
static bool g_CheckedMutex = false;

#define DBG_TESTSVPYIELDMUTEX() \
do { \
    if (!g_CheckedMutex) \
    { \
        assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr \
            && "This SvpSalInstance function requires use of SvpSalYieldMutex"); \
        g_CheckedMutex = true; \
    } \
while(false)

#else // NDEBUG
#define DBG_TESTSVPYIELDMUTEX() ((void)0)
#endif

#if !defined(ANDROID) && !defined(IOS) && !defined(EMSCRIPTEN)

static void atfork_child()
{
    if (SvpSalInstance::s_pDefaultInstance != nullptr)
    {
        SvpSalInstance::s_pDefaultInstance->CloseWakeupPipe();
    }
}

#endif

SvpSalInstance::SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex )
    : SalGenericInstance( std::move(pMutex) )
{
    m_aTimeout.tv_sec       = 0;
    m_aTimeout.tv_usec      = 0;
    m_nTimeoutMS            = 0;

    m_MainThread = osl::Thread::getCurrentIdentifier();
    if( s_pDefaultInstance == nullptr )
        s_pDefaultInstance = this;
#if !defined(ANDROID) && !defined(IOS) && !defined(EMSCRIPTEN)
    pthread_atfork(nullptr, nullptr, atfork_child);
#endif
#if defined EMSCRIPTEN
    ImplGetSVData()->maAppData.m_bUseSystemLoop = true;
#endif
}

SvpSalInstance::~SvpSalInstance()
{
    if( s_pDefaultInstance == this )
        s_pDefaultInstance = nullptr;
    CloseWakeupPipe();
}

void SvpSalInstance::CloseWakeupPipe()
{
    SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
    if (!pMutex)
        return;
    while (!pMutex->m_FeedbackPipe.empty())
        pMutex->m_FeedbackPipe.pop();
}

void SvpSalInstance::TriggerUserEventProcessing()
{
    Wakeup();
}

void SvpSalInstance::Wakeup(SvpRequest const request)
{
    DBG_TESTSVPYIELDMUTEX();

    ImplSVData* pSVData = ImplGetSVData();
    if (pSVData->mpWakeCallback && pSVData->mpPollClosure)
        pSVData->mpWakeCallback(pSVData->mpPollClosure);

    SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
    std::scoped_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
    if (request != SvpRequest::NONE)
        pMutex->m_Request = request;
    pMutex->m_wakeUpMain = true;
    pMutex->m_WakeUpMainCond.notify_one();
}

bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
{
    bool bRet = false;
    if( m_aTimeout.tv_sec ) // timer is started
    {
        timeval aTimeOfDay;
        gettimeofday( &aTimeOfDay, nullptr );
        if( aTimeOfDay >= m_aTimeout )
        {
            bRet = true;
            if( bExecuteTimers )
            {
                // timed out, update timeout
                m_aTimeout = aTimeOfDay;
                m_aTimeout += m_nTimeoutMS;

                osl::Guard< comphelper::SolarMutex > aGuard( GetYieldMutex() );

                // notify
                ImplSVData* pSVData = ImplGetSVData();
                if( pSVData->maSchedCtx.mpSalTimer )
                    pSVData->maSchedCtx.mpSalTimer->CallCallback();
            }
        }
    }
    return bRet;
}

SalFrame* SvpSalInstance::CreateChildFrame( SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle )
{
    return new SvpSalFrame( this, nullptr, nStyle );
}

SalFrame* SvpSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
{
    return new SvpSalFrame( this, pParent, nStyle );
}

void SvpSalInstance::DestroyFrame( SalFrame* pFrame )
{
    delete pFrame;
}

SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
{
    return new SvpSalObject;
}

void SvpSalInstance::DestroyObject( SalObject* pObject )
{
    delete pObject;
}

#ifndef IOS

std::unique_ptr<SalVirtualDevice> SvpSalInstance::CreateVirtualDevice(SalGraphics&&nbsp;rGraphics,
                                                       tools::Long nDX, tools::Long nDY,
                                                       DeviceFormat /*eFormat*/)
{
    SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(&rGraphics);
    assert(pSvpSalGraphics);
    std::unique_ptr<SalVirtualDevice> xNew(new SvpSalVirtualDevice(pSvpSalGraphics->getSurface(), /*pPreExistingTarget*/nullptr));
    if (!xNew->SetSize(nDX, nDY))
        xNew.reset();
    return xNew;
}

std::unique_ptr<SalVirtualDevice> SvpSalInstance::CreateVirtualDevice(SalGraphics&&nbsp;rGraphics,
                                                       tools::Long &nDX, tools::Long &nDY,
                                                       DeviceFormat /*eFormat*/,
                                                       const SystemGraphicsData& rGd)
{
    SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(&rGraphics);
    assert(pSvpSalGraphics);
#ifndef ANDROID
    // tdf#127529 normally pPreExistingTarget is null and we are a true virtualdevice drawing to a backing buffer.
    // Occasionally, for canvas/slideshow, pPreExistingTarget is pre-provided as a hack to use the vcl drawing
    // apis to render onto a preexisting cairo surface. The necessity for that precedes the use of cairo in vcl proper
    cairo_surface_t* pPreExistingTarget = static_cast<cairo_surface_t*>(rGd.pSurface);
#else
    //ANDROID case
    (void)rGd;
    cairo_surface_t* pPreExistingTarget = nullptr;
#endif
    std::unique_ptr<SalVirtualDevice> xNew(new SvpSalVirtualDevice(pSvpSalGraphics->getSurface(), pPreExistingTarget));
    if (!xNew->SetSize(nDX, nDY))
        xNew.reset();
    return xNew;
}

cairo_surface_t* get_underlying_cairo_surface(const VirtualDevice& rDevice)
{
    return static_cast<SvpSalVirtualDevice*>(rDevice.mpVirDev.get())->GetSurface();
}

const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
{
    static cairo_font_options_t *gOptions = nullptr;
    if (!gOptions)
    {
        gOptions = cairo_font_options_create();
        cairo_font_options_set_antialias(gOptions, CAIRO_ANTIALIAS_GRAY);
    }
    return gOptions;
}

#else // IOS

const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
{
    return nullptr;
}

#endif

SalTimer* SvpSalInstance::CreateSalTimer()
{
    return new SvpSalTimer( this );
}

SalSystem* SvpSalInstance::CreateSalSystem()
{
    return new SvpSalSystem();
}

std::shared_ptr<SalBitmap> SvpSalInstance::CreateSalBitmap()
{
#ifdef IOS
    return std::make_shared<QuartzSalBitmap>();
#else
    return std::make_shared<SvpSalBitmap>();
#endif
}

void SvpSalInstance::ProcessEvent( SalUserEvent aEvent )
{
    DBG_TESTSVPYIELDMUTEX();

    aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
    if( aEvent.m_nEvent == SalEvent::Resize )
    {
        // this would be a good time to post a paint
        const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame);
        pSvpFrame->PostPaint();
    }

    SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
    pMutex->m_NonMainWaitingYieldCond.set();
}

#if defined EMSCRIPTEN

static void loop(void * arg) {
    SolarMutexGuard g;
    static_cast<SvpSalInstance *>(arg)->ImplYield(falsefalse);
}

bool SvpSalInstance::DoExecute(int &) {
    assert(Application::IsUseSystemEventLoop());
    // emscripten_set_main_loop will unwind the stack by throwing a JavaScript exception, so we need
    // to manually undo the call of AcquireYieldMutex() done in InitVCL:
    ReleaseYieldMutex(false);
    // Somewhat randomly use an fps=100 argument so the loop callback is called 100 times per
    // second:
    emscripten_set_main_loop_arg(loop, this, 100, 1);
    O3TL_UNREACHABLE;
}

void SvpSalInstance::DoQuit() {
    assert(Application::IsUseSystemEventLoop());
}

#endif

SvpSalYieldMutex::SvpSalYieldMutex()
{
}

SvpSalYieldMutex::~SvpSalYieldMutex()
{
}

void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount)
{
    auto *const pInst = static_cast<SvpSalInstance*>(GetSalInstance());
    if (pInst && pInst->IsMainThread())
    {
        if (m_bNoYieldLock)
            return;

        do
        {
            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)
            {
                // 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;
                {
                    std::lock_guard lock(m_FeedbackMutex);
                    m_FeedbackPipe.push(bEvents);
                }
                m_FeedbackCV.notify_all();
            }
        }
        while (true);
    }
    else
    {
        m_aMutex.acquire();
    }
    ++m_nCount;
    SalYieldMutex::doAcquire(nLockCount - 1);
}

sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll)
{
    auto *const pInst = static_cast<SvpSalInstance*>(GetSalInstance());
    if (pInst && pInst->IsMainThread())
    {
        if (m_bNoYieldLock)
            return 1;
        else
            return SalYieldMutex::doRelease(bUnlockAll);
    }
    sal_uInt32 nCount;
    {
        // read m_nCount before doRelease
        bool const isReleased(bUnlockAll || m_nCount == 1);
        nCount = comphelper::SolarMutex::doRelease( bUnlockAll );

        if (isReleased)
        {
            if (vcl::lok::isUnipoll())
            {
                if (pInst)
                    pInst->Wakeup();
            }
            else
            {
                std::scoped_lock<std::mutex> g(m_WakeUpMainMutex);
                m_wakeUpMain = true;
                m_WakeUpMainCond.notify_one();
            }
        }
    }
    return nCount;
}

bool SvpSalYieldMutex::IsCurrentThread() const
{
    if (GetSalInstance()->IsMainThread() && m_bNoYieldLock)
        return true;
    else
        return SalYieldMutex::IsCurrentThread();
}

bool SvpSalInstance::IsMainThread() const
{
    return osl::Thread::getCurrentIdentifier() == m_MainThread;
}

void SvpSalInstance::updateMainThread()
{
    if (!IsMainThread())
    {
        m_MainThread = osl::Thread::getCurrentIdentifier();
        ImplGetSVData()->mnMainThreadId = osl::Thread::getCurrentIdentifier();
    }
}

bool SvpSalInstance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
{
    DBG_TESTSVPYIELDMUTEX();
    DBG_TESTSOLARMUTEX();
    assert(IsMainThread());

    bool bWasEvent = DispatchUserEvents(bHandleAllCurrentEvents);
    if (!bHandleAllCurrentEvents && bWasEvent)
        return true;

    // CheckTimeout() invokes the sal timer, which invokes the scheduler.
    bWasEvent = CheckTimeout() || bWasEvent;
    const bool bMustSleep = bWait && !bWasEvent;

    // This is wrong and must be removed!
    // We always want to drop the SolarMutex on yield; that is the whole point of yield.
    // If we know the LOK client has pending input events, then don't yet return, so those events
    // can be processed as well.
    if (!bMustSleep && !comphelper::LibreOfficeKit::anyInput())
        return bWasEvent;

    sal_Int64 nTimeoutMicroS = 0;
    if (bMustSleep)
    {
        if (m_aTimeout.tv_sec) // Timer is started.
        {
            timeval Timeout;
            // determine remaining timeout.
            gettimeofday (&Timeout, nullptr);
            if (m_aTimeout > Timeout)
                nTimeoutMicroS = ((m_aTimeout.tv_sec - Timeout.tv_sec) * 1000 * 1000 +
                                  (m_aTimeout.tv_usec - Timeout.tv_usec));
        }
        else
            nTimeoutMicroS = -1; // wait until something happens
    }

    SolarMutexReleaser aReleaser;

    if (vcl::lok::isUnipoll())
    {
        ImplSVData* pSVData = ImplGetSVData();
        if (pSVData->mpPollClosure)
        {
            int nPollResult = pSVData->mpPollCallback(pSVData->mpPollClosure, nTimeoutMicroS);
            if (nPollResult < 0)
                pSVData->maAppData.mbAppQuit = true;
            bWasEvent = bWasEvent || (nPollResult != 0);
        }
    }
    else if (bMustSleep)
    {
        SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
        std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
        // wait for doRelease() or Wakeup() to set the condition
        if (nTimeoutMicroS == -1)
        {
            pMutex->m_WakeUpMainCond.wait(g,
                    [pMutex]() { return pMutex->m_wakeUpMain; });
        }
        else
        {
            int nTimeoutMS = nTimeoutMicroS / 1000;
            if (nTimeoutMicroS % 1000)
                nTimeoutMS += 1;
            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
    }

    return bWasEvent;
}

bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
{
    DBG_TESTSVPYIELDMUTEX();
    DBG_TESTSOLARMUTEX();

    bool bWasEvent(false);
    SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));

    if (IsMainThread())
    {
        bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
        if (bWasEvent)
            pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads
    }
    else
    {
        // TODO: use a SolarMutexReleaser here and drop the m_bNoYieldLock usage
        Wakeup(bHandleAllCurrentEvents
                ? SvpRequest::MainThreadDispatchAllEvents
                : SvpRequest::MainThreadDispatchOneEvent);

        // blocking read (for synchronisation)
        {
            std::unique_lock lock(pMutex->m_FeedbackMutex);
            pMutex->m_FeedbackCV.wait(lock, [pMutex] { return !pMutex->m_FeedbackPipe.empty(); });
            bWasEvent = pMutex->m_FeedbackPipe.front();
            pMutex->m_FeedbackPipe.pop();
        }
        if (!bWasEvent && bWait)
        {
            // block & release YieldMutex until the main thread does something
            pMutex->m_NonMainWaitingYieldCond.reset();
            SolarMutexReleaser aReleaser;
            pMutex->m_NonMainWaitingYieldCond.wait();
        }
    }

    return bWasEvent;
}

bool SvpSalInstance::AnyInput( VclInputFlags nType )
{
    if( nType & VclInputFlags::TIMER )
        return CheckTimeout( false );
    return false;
}

OUString SvpSalInstance::GetConnectionIdentifier()
{
    return OUString();
}

void SvpSalInstance::StopTimer()
{
    m_aTimeout.tv_sec   = 0;
    m_aTimeout.tv_usec  = 0;
    m_nTimeoutMS        = 0;
}

void SvpSalInstance::StartTimer( sal_uInt64 nMS )
{
    timeval aPrevTimeout (m_aTimeout);
    gettimeofday (&m_aTimeout, nullptr);

    m_nTimeoutMS  = nMS;
    m_aTimeout    += m_nTimeoutMS;

    if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
    {
        // Wakeup from previous timeout (or stopped timer).
        Wakeup();
    }
}

void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
{
}

SvpSalTimer::~SvpSalTimer()
{
}

void SvpSalTimer::Stop()
{
    m_pInstance->StopTimer();
}

void SvpSalTimer::Start( sal_uInt64 nMS )
{
    m_pInstance->StartTimer( nMS );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=60 H=95 G=79

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.