mirror of
https://github.com/LibreOffice/core.git
synced 2025-08-07 11:19:28 +00:00

WARNING: ThreadSanitizer: data race (pid=271122) Write of size 8 at 0x7244000becc0 by main thread (mutexes: write M0): #0 drawinglayer::primitive2d::BufferedDecompositionPrimitive2D::get2DDecomposition(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&, drawinglayer::geometry::ViewInformation2D const&) const /home/noel/libo-tsan/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx:90 (libdrawinglayercorelo.so+0xb986) #1 drawinglayer::processor2d::BaseProcessor2D::process(drawinglayer::primitive2d::BasePrimitive2D const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/baseprocessor2d.cxx:43 (libdrawinglayerlo.so+0x1284cf) #2 drawinglayer::processor2d::CairoPixelProcessor2D::processBasePrimitive2D(drawinglayer::primitive2d::BasePrimitive2D const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx:4230 (libdrawinglayerlo.so+0x7a370) #3 drawinglayer::processor2d::BaseProcessor2D::process(drawinglayer::primitive2d::Primitive2DContainer const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/baseprocessor2d.cxx:67 (libdrawinglayerlo.so+0x1286f1) #4 drawinglayer::processor2d::CairoPixelProcessor2D::processMaskPrimitive2D(drawinglayer::primitive2d::MaskPrimitive2D const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx:1919 (libdrawinglayerlo.so+0x6fbd9) #5 drawinglayer::processor2d::CairoPixelProcessor2D::processBasePrimitive2D(drawinglayer::primitive2d::BasePrimitive2D const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx:4103 (libdrawinglayerlo.so+0x7a33c) #6 drawinglayer::processor2d::BaseProcessor2D::process(drawinglayer::primitive2d::Primitive2DContainer const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/baseprocessor2d.cxx:67 (libdrawinglayerlo.so+0x1285f1) #7 drawinglayer::processor2d::BaseProcessor2D::visit(drawinglayer::primitive2d::Primitive2DContainer const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/baseprocessor2d.cxx:54 (libdrawinglayerlo.so+0x1285f1) #8 drawinglayer::primitive2d::GroupPrimitive2D::getChildren(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const /home/noel/libo-tsan/include/drawinglayer/primitive2d/groupprimitive2d.hxx:76 (libdrawinglayercorelo.so+0x1c51b) #9 drawinglayer::primitive2d::GroupPrimitive2D::get2DDecomposition(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&, drawinglayer::geometry::ViewInformation2D const&) const /home/noel/libo-tsan/drawinglayer/source/primitive2d/groupprimitive2d.cxx:53 (libdrawinglayercorelo.so+0x1c51b) #10 drawinglayer::processor2d::BaseProcessor2D::process(drawinglayer::primitive2d::BasePrimitive2D const&) /home/noel/libo-tsan/drawinglayer/source/processor2d/baseprocessor2d.cxx:43 (libdrawinglayerlo.so+0x1284cf) Previous read of size 8 at 0x7244000becc0 by thread T299 (mutexes: write M1): #0 std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >::time_since_epoch() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/chrono.h:950 (libdrawinglayercorelo.so+0xcd6f) #1 std::common_type<std::chrono::duration<long, std::ratio<1l, 1000000000l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >::type std::chrono::operator-<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > > const&, std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > > const&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/chrono.h:1143 (libdrawinglayercorelo.so+0xcd6f) #2 drawinglayer::primitive2d::BufferedDecompositionFlusher::run() /home/noel/libo-tsan/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx:133 (libdrawinglayercorelo.so+0xcd6f) #3 threadFunc /home/noel/libo-tsan/include/osl/thread.hxx:189 (libdrawinglayercorelo.so+0xe0ee) #4 osl_thread_start_Impl(void*) /home/noel/libo-tsan/sal/osl/unx/thread.cxx:237 (libuno_sal.so.3+0x6b419) Change-Id: Ie7afe0501d0d562117fc4418a3fe24853397aefe Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187238 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
179 lines
6.0 KiB
C++
179 lines
6.0 KiB
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 <sal/log.hxx>
|
|
#include <comphelper/solarmutex.hxx>
|
|
#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
|
|
|
|
namespace drawinglayer::primitive2d
|
|
{
|
|
/**
|
|
This is a "garbage collection" approach to flushing.
|
|
|
|
We store entries in a set. Every 2 seconds, we scan the set for entries that have not
|
|
been used for 10 seconds or more, and if so, we flush the buffer primitives in those entries.
|
|
|
|
This mechanism is __deliberately__ not perfect.
|
|
Sometimes things will be flushed a little too soon, sometimes things will wait a little too long,
|
|
since we only have a granularity of 2 seconds.
|
|
But what is gains from not being perfect, is scalability.
|
|
|
|
It is very simple, scales to lots and lots of primitives without needing lots of timers, and performs
|
|
very little work in the common case.
|
|
|
|
Shutdown notes
|
|
--------------------
|
|
The process of handling shutdown is more complicated here than it should be, because we are interacting with
|
|
various vcl-level things (by virtue of calling into drawinglayer primitives that use vcl facilities), but we
|
|
do not have access to vcl-level API here (like SolarMutexReleaser and vcl::Timer).
|
|
*/
|
|
|
|
static BufferedDecompositionFlusher* getInstance()
|
|
{
|
|
static std::unique_ptr<BufferedDecompositionFlusher> gaTimer(new BufferedDecompositionFlusher);
|
|
return gaTimer.get();
|
|
}
|
|
|
|
// static
|
|
void BufferedDecompositionFlusher::shutdown()
|
|
{
|
|
BufferedDecompositionFlusher* pFlusher = getInstance();
|
|
pFlusher->onTeardown();
|
|
// We have to wait for the thread to exit, otherwise we might end up with the background thread
|
|
// trying to process stuff while it has things ripped out underneath it.
|
|
pFlusher->join();
|
|
}
|
|
|
|
// static
|
|
void BufferedDecompositionFlusher::update(const BufferedDecompositionPrimitive2D* p)
|
|
{
|
|
getInstance()->updateImpl(p);
|
|
}
|
|
|
|
// static
|
|
void BufferedDecompositionFlusher::update(const BufferedDecompositionGroupPrimitive2D* p)
|
|
{
|
|
getInstance()->updateImpl(p);
|
|
}
|
|
|
|
// static
|
|
void BufferedDecompositionFlusher::remove(const BufferedDecompositionPrimitive2D* p)
|
|
{
|
|
getInstance()->removeImpl(p);
|
|
}
|
|
|
|
// static
|
|
void BufferedDecompositionFlusher::remove(const BufferedDecompositionGroupPrimitive2D* p)
|
|
{
|
|
getInstance()->removeImpl(p);
|
|
}
|
|
|
|
BufferedDecompositionFlusher::BufferedDecompositionFlusher() { create(); }
|
|
|
|
void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionPrimitive2D* p)
|
|
{
|
|
std::unique_lock l(maMutex);
|
|
if (!mbShutdown)
|
|
maRegistered1.insert(const_cast<BufferedDecompositionPrimitive2D*>(p));
|
|
}
|
|
|
|
void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionGroupPrimitive2D* p)
|
|
{
|
|
std::unique_lock l(maMutex);
|
|
if (!mbShutdown)
|
|
maRegistered2.insert(const_cast<BufferedDecompositionGroupPrimitive2D*>(p));
|
|
}
|
|
|
|
void BufferedDecompositionFlusher::removeImpl(const BufferedDecompositionPrimitive2D* p)
|
|
{
|
|
std::unique_lock l(maMutex);
|
|
if (!mbShutdown)
|
|
maRegistered1.erase(const_cast<BufferedDecompositionPrimitive2D*>(p));
|
|
}
|
|
|
|
void BufferedDecompositionFlusher::removeImpl(const BufferedDecompositionGroupPrimitive2D* p)
|
|
{
|
|
std::unique_lock l(maMutex);
|
|
if (!mbShutdown)
|
|
maRegistered2.erase(const_cast<BufferedDecompositionGroupPrimitive2D*>(p));
|
|
}
|
|
|
|
void SAL_CALL BufferedDecompositionFlusher::run()
|
|
{
|
|
setName("BufferedDecompositionFlusher");
|
|
for (;;)
|
|
{
|
|
auto aNow = std::chrono::steady_clock::now();
|
|
std::vector<rtl::Reference<BufferedDecompositionPrimitive2D>> aRemoved1;
|
|
std::vector<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> aRemoved2;
|
|
{
|
|
std::unique_lock l1(maMutex);
|
|
// exit if we have been shutdown
|
|
if (mbShutdown)
|
|
break;
|
|
for (auto it = maRegistered1.begin(); it != maRegistered1.end();)
|
|
{
|
|
if (aNow - (*it)->maLastAccess.load() > std::chrono::seconds(10))
|
|
{
|
|
aRemoved1.push_back(*it);
|
|
it = maRegistered1.erase(it);
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
for (auto it = maRegistered2.begin(); it != maRegistered2.end();)
|
|
{
|
|
if (aNow - (*it)->maLastAccess.load() > std::chrono::seconds(10))
|
|
{
|
|
aRemoved2.push_back(*it);
|
|
it = maRegistered2.erase(it);
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
}
|
|
|
|
{
|
|
// some parts of skia do not take kindly to being accessed from multiple threads
|
|
osl::Guard<comphelper::SolarMutex> aGuard(comphelper::SolarMutex::get());
|
|
|
|
for (const auto& r : aRemoved1)
|
|
r->setBuffered2DDecomposition(nullptr);
|
|
for (const auto& r : aRemoved2)
|
|
r->setBuffered2DDecomposition(Primitive2DContainer{});
|
|
}
|
|
|
|
wait(TimeValue(2, 0));
|
|
}
|
|
}
|
|
|
|
/// Only called by FlusherDeinit
|
|
void BufferedDecompositionFlusher::onTeardown()
|
|
{
|
|
std::unique_lock l2(maMutex);
|
|
mbShutdown = true;
|
|
maRegistered1.clear();
|
|
maRegistered2.clear();
|
|
}
|
|
|
|
} // end of namespace drawinglayer::primitive2d
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|