Coverage Report

Created: 2025-12-31 10:39

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