Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <sal/config.h>
21
#include <sal/log.hxx>
22
#include <comphelper/solarmutex.hxx>
23
#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
24
25
namespace drawinglayer::primitive2d
26
{
27
/**
28
    This is a "garbage collection" approach to flushing.
29
30
    We store entries in a set. Every 2 seconds, we scan the set for entries that have not
31
    been used for 10 seconds or more, and if so, we flush the buffer primitives in those entries.
32
33
    This mechanism is __deliberately__ not perfect.
34
    Sometimes things will be flushed a little too soon, sometimes things will wait a little too long,
35
    since we only have a granularity of 2 seconds.
36
    But what is gains from not being perfect, is scalability.
37
38
    It is very simple, scales to lots and lots of primitives without needing lots of timers, and performs
39
    very little work in the common case.
40
41
    Shutdown notes
42
    --------------------
43
    The process of handling shutdown is more complicated here than it should be, because we are interacting with
44
    various vcl-level things (by virtue of calling into drawinglayer primitives that use vcl facilities), but we
45
    do not have access to vcl-level API here (like SolarMutexReleaser and vcl::Timer).
46
*/
47
48
static BufferedDecompositionFlusher* getInstance()
49
0
{
50
0
    static std::unique_ptr<BufferedDecompositionFlusher> gaTimer(new BufferedDecompositionFlusher);
51
0
    return gaTimer.get();
52
0
}
53
54
// static
55
void BufferedDecompositionFlusher::shutdown()
56
0
{
57
0
    BufferedDecompositionFlusher* pFlusher = getInstance();
58
0
    pFlusher->onTeardown();
59
    // We have to wait for the thread to exit, otherwise we might end up with the background thread
60
    // trying to process stuff while it has things ripped out underneath it.
61
0
    pFlusher->join();
62
0
}
63
64
// static
65
void BufferedDecompositionFlusher::update(const BufferedDecompositionPrimitive2D* p)
66
0
{
67
0
    getInstance()->updateImpl(p);
68
0
}
69
70
// static
71
void BufferedDecompositionFlusher::update(const BufferedDecompositionGroupPrimitive2D* p)
72
0
{
73
0
    getInstance()->updateImpl(p);
74
0
}
75
76
// static
77
void BufferedDecompositionFlusher::remove(const BufferedDecompositionPrimitive2D* p)
78
0
{
79
0
    getInstance()->removeImpl(p);
80
0
}
81
82
// static
83
void BufferedDecompositionFlusher::remove(const BufferedDecompositionGroupPrimitive2D* p)
84
0
{
85
0
    getInstance()->removeImpl(p);
86
0
}
87
88
0
BufferedDecompositionFlusher::BufferedDecompositionFlusher() { create(); }
89
90
void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionPrimitive2D* p)
91
0
{
92
0
    std::unique_lock l(maMutex);
93
0
    if (!mbShutdown)
94
0
        maRegistered1.insert(const_cast<BufferedDecompositionPrimitive2D*>(p));
95
0
}
96
97
void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionGroupPrimitive2D* p)
98
0
{
99
0
    std::unique_lock l(maMutex);
100
0
    if (!mbShutdown)
101
0
        maRegistered2.insert(const_cast<BufferedDecompositionGroupPrimitive2D*>(p));
102
0
}
103
104
void BufferedDecompositionFlusher::removeImpl(const BufferedDecompositionPrimitive2D* p)
105
0
{
106
0
    std::unique_lock l(maMutex);
107
0
    if (!mbShutdown)
108
0
        maRegistered1.erase(const_cast<BufferedDecompositionPrimitive2D*>(p));
109
0
}
110
111
void BufferedDecompositionFlusher::removeImpl(const BufferedDecompositionGroupPrimitive2D* p)
112
0
{
113
0
    std::unique_lock l(maMutex);
114
0
    if (!mbShutdown)
115
0
        maRegistered2.erase(const_cast<BufferedDecompositionGroupPrimitive2D*>(p));
116
0
}
117
118
void SAL_CALL BufferedDecompositionFlusher::run()
119
0
{
120
0
    setName("BufferedDecompositionFlusher");
121
0
    for (;;)
122
0
    {
123
0
        auto aNow = std::chrono::steady_clock::now();
124
0
        std::vector<rtl::Reference<BufferedDecompositionPrimitive2D>> aRemoved1;
125
0
        std::vector<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> aRemoved2;
126
0
        {
127
0
            std::unique_lock l1(maMutex);
128
            // exit if we have been shutdown
129
0
            if (mbShutdown)
130
0
                break;
131
0
            for (auto it = maRegistered1.begin(); it != maRegistered1.end();)
132
0
            {
133
0
                if (aNow - (*it)->maLastAccess.load() > std::chrono::seconds(10))
134
0
                {
135
0
                    aRemoved1.push_back(*it);
136
0
                    it = maRegistered1.erase(it);
137
0
                }
138
0
                else
139
0
                    ++it;
140
0
            }
141
0
            for (auto it = maRegistered2.begin(); it != maRegistered2.end();)
142
0
            {
143
0
                if (aNow - (*it)->maLastAccess.load() > std::chrono::seconds(10))
144
0
                {
145
0
                    aRemoved2.push_back(*it);
146
0
                    it = maRegistered2.erase(it);
147
0
                }
148
0
                else
149
0
                    ++it;
150
0
            }
151
0
        }
152
153
0
        {
154
            // some parts of skia do not take kindly to being accessed from multiple threads
155
0
            osl::Guard<comphelper::SolarMutex> aGuard(comphelper::SolarMutex::get());
156
157
0
            for (const auto& r : aRemoved1)
158
0
                r->setBuffered2DDecomposition(nullptr);
159
0
            for (const auto& r : aRemoved2)
160
0
                r->setBuffered2DDecomposition(Primitive2DContainer{});
161
0
        }
162
163
0
        wait(TimeValue(2, 0));
164
0
    }
165
0
}
166
167
/// Only called by FlusherDeinit
168
void BufferedDecompositionFlusher::onTeardown()
169
0
{
170
0
    std::unique_lock l2(maMutex);
171
0
    mbShutdown = true;
172
0
    maRegistered1.clear();
173
0
    maRegistered2.clear();
174
0
}
175
176
} // end of namespace drawinglayer::primitive2d
177
178
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */