Avoid potential negative array index access to cached text.
[LibreOffice.git] / vcl / headless / svpinst.cxx
blob19eef8997689f6fd848f4c1b05207d9fed372fd9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
21 #include <sal/config.h>
23 #include <mutex>
25 #include <pthread.h>
26 #include <sys/time.h>
27 #include <poll.h>
29 #include <sal/types.h>
30 #include <sal/log.hxx>
32 #include <vcl/virdev.hxx>
33 #include <vcl/inputtypes.hxx>
34 #include <vcl/lok.hxx>
36 #include <headless/svpinst.hxx>
37 #include <headless/svpframe.hxx>
38 #include <headless/svpdummies.hxx>
39 #include <headless/svpvd.hxx>
40 #ifdef IOS
41 # include <quartz/salbmp.h>
42 # include <quartz/salgdi.h>
43 # include <quartz/salvd.h>
44 #else
45 # include <cairo.h>
46 # include <headless/svpgdi.hxx>
47 #endif
48 #include <headless/svpbmp.hxx>
50 #include <salframe.hxx>
51 #include <svdata.hxx>
52 // FIXME: remove when we re-work the svp mainloop
53 #include <unx/salunxtime.h>
54 #include <comphelper/lok.hxx>
55 #include <tools/debug.hxx>
57 SvpSalInstance* SvpSalInstance::s_pDefaultInstance = nullptr;
59 #ifndef NDEBUG
60 static bool g_CheckedMutex = false;
62 #define DBG_TESTSVPYIELDMUTEX() \
63 do { \
64 if (!g_CheckedMutex) \
65 { \
66 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr \
67 && "This SvpSalInstance function requires use of SvpSalYieldMutex"); \
68 g_CheckedMutex = true; \
69 } \
70 } while(false)
72 #else // NDEBUG
73 #define DBG_TESTSVPYIELDMUTEX() ((void)0)
74 #endif
76 #if !defined(ANDROID) && !defined(IOS) && !defined(EMSCRIPTEN)
78 static void atfork_child()
80 if (SvpSalInstance::s_pDefaultInstance != nullptr)
82 SvpSalInstance::s_pDefaultInstance->CloseWakeupPipe();
86 #endif
88 SvpSalInstance::SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex )
89 : SalGenericInstance( std::move(pMutex) )
91 m_aTimeout.tv_sec = 0;
92 m_aTimeout.tv_usec = 0;
93 m_nTimeoutMS = 0;
95 m_MainThread = osl::Thread::getCurrentIdentifier();
96 if( s_pDefaultInstance == nullptr )
97 s_pDefaultInstance = this;
98 #if !defined(ANDROID) && !defined(IOS) && !defined(EMSCRIPTEN)
99 pthread_atfork(nullptr, nullptr, atfork_child);
100 #endif
103 SvpSalInstance::~SvpSalInstance()
105 if( s_pDefaultInstance == this )
106 s_pDefaultInstance = nullptr;
107 CloseWakeupPipe();
110 void SvpSalInstance::CloseWakeupPipe()
112 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
113 if (!pMutex)
114 return;
115 while (!pMutex->m_FeedbackPipe.empty())
116 pMutex->m_FeedbackPipe.pop();
119 void SvpSalInstance::TriggerUserEventProcessing()
121 Wakeup();
124 void SvpSalInstance::Wakeup(SvpRequest const request)
126 DBG_TESTSVPYIELDMUTEX();
128 ImplSVData* pSVData = ImplGetSVData();
129 if (pSVData->mpWakeCallback && pSVData->mpPollClosure)
130 pSVData->mpWakeCallback(pSVData->mpPollClosure);
132 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
133 std::scoped_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
134 if (request != SvpRequest::NONE)
135 pMutex->m_Request = request;
136 pMutex->m_wakeUpMain = true;
137 pMutex->m_WakeUpMainCond.notify_one();
140 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
142 bool bRet = false;
143 if( m_aTimeout.tv_sec ) // timer is started
145 timeval aTimeOfDay;
146 gettimeofday( &aTimeOfDay, nullptr );
147 if( aTimeOfDay >= m_aTimeout )
149 bRet = true;
150 if( bExecuteTimers )
152 // timed out, update timeout
153 m_aTimeout = aTimeOfDay;
154 m_aTimeout += m_nTimeoutMS;
156 osl::Guard< comphelper::SolarMutex > aGuard( GetYieldMutex() );
158 // notify
159 ImplSVData* pSVData = ImplGetSVData();
160 if( pSVData->maSchedCtx.mpSalTimer )
161 pSVData->maSchedCtx.mpSalTimer->CallCallback();
165 return bRet;
168 SalFrame* SvpSalInstance::CreateChildFrame( SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle )
170 return new SvpSalFrame( this, nullptr, nStyle );
173 SalFrame* SvpSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
175 return new SvpSalFrame( this, pParent, nStyle );
178 void SvpSalInstance::DestroyFrame( SalFrame* pFrame )
180 delete pFrame;
183 SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
185 return new SvpSalObject;
188 void SvpSalInstance::DestroyObject( SalObject* pObject )
190 delete pObject;
193 #ifndef IOS
195 std::unique_ptr<SalVirtualDevice> SvpSalInstance::CreateVirtualDevice(SalGraphics& rGraphics,
196 tools::Long &nDX, tools::Long &nDY,
197 DeviceFormat /*eFormat*/,
198 const SystemGraphicsData* pGd)
200 SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(&rGraphics);
201 assert(pSvpSalGraphics);
202 #ifndef ANDROID
203 // tdf#127529 normally pPreExistingTarget is null and we are a true virtualdevice drawing to a backing buffer.
204 // Occasionally, for canvas/slideshow, pPreExistingTarget is pre-provided as a hack to use the vcl drawing
205 // apis to render onto a preexisting cairo surface. The necessity for that precedes the use of cairo in vcl proper
206 cairo_surface_t* pPreExistingTarget = pGd ? static_cast<cairo_surface_t*>(pGd->pSurface) : nullptr;
207 #else
208 //ANDROID case
209 (void)pGd;
210 cairo_surface_t* pPreExistingTarget = nullptr;
211 #endif
212 std::unique_ptr<SalVirtualDevice> xNew(new SvpSalVirtualDevice(pSvpSalGraphics->getSurface(), pPreExistingTarget));
213 if (!xNew->SetSize(nDX, nDY))
214 xNew.reset();
215 return xNew;
218 cairo_surface_t* get_underlying_cairo_surface(const VirtualDevice& rDevice)
220 return static_cast<SvpSalVirtualDevice*>(rDevice.mpVirDev.get())->GetSurface();
223 const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
225 static cairo_font_options_t *gOptions = nullptr;
226 if (!gOptions)
228 gOptions = cairo_font_options_create();
229 cairo_font_options_set_antialias(gOptions, CAIRO_ANTIALIAS_GRAY);
231 return gOptions;
234 #else // IOS
236 const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
238 return nullptr;
241 #endif
243 SalTimer* SvpSalInstance::CreateSalTimer()
245 return new SvpSalTimer( this );
248 SalSystem* SvpSalInstance::CreateSalSystem()
250 return new SvpSalSystem();
253 std::shared_ptr<SalBitmap> SvpSalInstance::CreateSalBitmap()
255 #ifdef IOS
256 return std::make_shared<QuartzSalBitmap>();
257 #else
258 return std::make_shared<SvpSalBitmap>();
259 #endif
262 void SvpSalInstance::ProcessEvent( SalUserEvent aEvent )
264 DBG_TESTSVPYIELDMUTEX();
266 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
267 if( aEvent.m_nEvent == SalEvent::Resize )
269 // this would be a good time to post a paint
270 const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame);
271 pSvpFrame->PostPaint();
274 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
275 pMutex->m_NonMainWaitingYieldCond.set();
278 SvpSalYieldMutex::SvpSalYieldMutex()
282 SvpSalYieldMutex::~SvpSalYieldMutex()
286 void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount)
288 auto *const pInst = static_cast<SvpSalInstance*>(GetSalInstance());
289 if (pInst && pInst->IsMainThread())
291 if (m_bNoYieldLock)
292 return;
296 SvpRequest request = SvpRequest::NONE;
298 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
299 if (m_aMutex.tryToAcquire()) {
300 // if there's a request, the other thread holds m_aMutex
301 assert(m_Request == SvpRequest::NONE);
302 m_wakeUpMain = false;
303 break;
305 m_WakeUpMainCond.wait(g, [this]() { return m_wakeUpMain; });
306 m_wakeUpMain = false;
307 std::swap(m_Request, request);
309 if (request != SvpRequest::NONE)
311 // nested Yield on behalf of another thread
312 assert(!m_bNoYieldLock);
313 m_bNoYieldLock = true;
314 bool const bEvents = pInst->DoYield(false, request == SvpRequest::MainThreadDispatchAllEvents);
315 m_bNoYieldLock = false;
317 std::lock_guard lock(m_FeedbackMutex);
318 m_FeedbackPipe.push(bEvents);
320 m_FeedbackCV.notify_all();
323 while (true);
325 else
327 m_aMutex.acquire();
329 ++m_nCount;
330 SalYieldMutex::doAcquire(nLockCount - 1);
333 sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll)
335 auto *const pInst = static_cast<SvpSalInstance*>(GetSalInstance());
336 if (pInst && pInst->IsMainThread())
338 if (m_bNoYieldLock)
339 return 1;
340 else
341 return SalYieldMutex::doRelease(bUnlockAll);
343 sal_uInt32 nCount;
345 // read m_nCount before doRelease
346 bool const isReleased(bUnlockAll || m_nCount == 1);
347 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
349 if (isReleased)
351 if (vcl::lok::isUnipoll())
353 if (pInst)
354 pInst->Wakeup();
356 else
358 std::scoped_lock<std::mutex> g(m_WakeUpMainMutex);
359 m_wakeUpMain = true;
360 m_WakeUpMainCond.notify_one();
364 return nCount;
367 bool SvpSalYieldMutex::IsCurrentThread() const
369 if (GetSalInstance()->IsMainThread() && m_bNoYieldLock)
370 return true;
371 else
372 return SalYieldMutex::IsCurrentThread();
375 bool SvpSalInstance::IsMainThread() const
377 return osl::Thread::getCurrentIdentifier() == m_MainThread;
380 void SvpSalInstance::updateMainThread()
382 if (!IsMainThread())
384 m_MainThread = osl::Thread::getCurrentIdentifier();
385 ImplGetSVData()->mnMainThreadId = osl::Thread::getCurrentIdentifier();
389 bool SvpSalInstance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
391 DBG_TESTSVPYIELDMUTEX();
392 DBG_TESTSOLARMUTEX();
393 assert(IsMainThread());
395 bool bWasEvent = DispatchUserEvents(bHandleAllCurrentEvents);
396 if (!bHandleAllCurrentEvents && bWasEvent)
397 return true;
399 bWasEvent = CheckTimeout() || bWasEvent;
400 const bool bMustSleep = bWait && !bWasEvent;
402 // This is wrong and must be removed!
403 // We always want to drop the SolarMutex on yield; that is the whole point of yield.
404 if (!bMustSleep)
405 return bWasEvent;
407 sal_Int64 nTimeoutMicroS = 0;
408 if (bMustSleep)
410 if (m_aTimeout.tv_sec) // Timer is started.
412 timeval Timeout;
413 // determine remaining timeout.
414 gettimeofday (&Timeout, nullptr);
415 if (m_aTimeout > Timeout)
416 nTimeoutMicroS = ((m_aTimeout.tv_sec - Timeout.tv_sec) * 1000 * 1000 +
417 (m_aTimeout.tv_usec - Timeout.tv_usec));
419 else
420 nTimeoutMicroS = -1; // wait until something happens
423 SolarMutexReleaser aReleaser;
425 if (vcl::lok::isUnipoll())
427 ImplSVData* pSVData = ImplGetSVData();
428 if (pSVData->mpPollClosure)
430 int nPollResult = pSVData->mpPollCallback(pSVData->mpPollClosure, nTimeoutMicroS);
431 if (nPollResult < 0)
432 pSVData->maAppData.mbAppQuit = true;
433 bWasEvent = bWasEvent || (nPollResult != 0);
436 else if (bMustSleep)
438 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
439 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
440 // wait for doRelease() or Wakeup() to set the condition
441 if (nTimeoutMicroS == -1)
443 pMutex->m_WakeUpMainCond.wait(g,
444 [pMutex]() { return pMutex->m_wakeUpMain; });
446 else
448 int nTimeoutMS = nTimeoutMicroS / 1000;
449 if (nTimeoutMicroS % 1000)
450 nTimeoutMS += 1;
451 pMutex->m_WakeUpMainCond.wait_for(g,
452 std::chrono::milliseconds(nTimeoutMS),
453 [pMutex]() { return pMutex->m_wakeUpMain; });
455 // here no need to check m_Request because Acquire will do it
458 return bWasEvent;
461 bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
463 DBG_TESTSVPYIELDMUTEX();
464 DBG_TESTSOLARMUTEX();
466 bool bWasEvent(false);
467 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
469 if (IsMainThread())
471 bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
472 if (bWasEvent)
473 pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads
475 else
477 // TODO: use a SolarMutexReleaser here and drop the m_bNoYieldLock usage
478 Wakeup(bHandleAllCurrentEvents
479 ? SvpRequest::MainThreadDispatchAllEvents
480 : SvpRequest::MainThreadDispatchOneEvent);
482 // blocking read (for synchronisation)
484 std::unique_lock lock(pMutex->m_FeedbackMutex);
485 pMutex->m_FeedbackCV.wait(lock, [pMutex] { return !pMutex->m_FeedbackPipe.empty(); });
486 bWasEvent = pMutex->m_FeedbackPipe.front();
487 pMutex->m_FeedbackPipe.pop();
489 if (!bWasEvent && bWait)
491 // block & release YieldMutex until the main thread does something
492 pMutex->m_NonMainWaitingYieldCond.reset();
493 SolarMutexReleaser aReleaser;
494 pMutex->m_NonMainWaitingYieldCond.wait();
498 return bWasEvent;
501 bool SvpSalInstance::AnyInput( VclInputFlags nType )
503 if( nType & VclInputFlags::TIMER )
504 return CheckTimeout( false );
505 return false;
508 OUString SvpSalInstance::GetConnectionIdentifier()
510 return OUString();
513 void SvpSalInstance::StopTimer()
515 m_aTimeout.tv_sec = 0;
516 m_aTimeout.tv_usec = 0;
517 m_nTimeoutMS = 0;
520 void SvpSalInstance::StartTimer( sal_uInt64 nMS )
522 timeval aPrevTimeout (m_aTimeout);
523 gettimeofday (&m_aTimeout, nullptr);
525 m_nTimeoutMS = nMS;
526 m_aTimeout += m_nTimeoutMS;
528 if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
530 // Wakeup from previous timeout (or stopped timer).
531 Wakeup();
535 void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
539 SvpSalTimer::~SvpSalTimer()
543 void SvpSalTimer::Stop()
545 m_pInstance->StopTimer();
548 void SvpSalTimer::Start( sal_uInt64 nMS )
550 m_pInstance->StartTimer( nMS );
553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */