File: referencecounting.h

package info (click to toggle)
kdevelop 4%3A22.12.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 70,096 kB
  • sloc: cpp: 284,635; javascript: 3,558; python: 3,422; sh: 1,319; ansic: 685; xml: 331; php: 95; lisp: 66; makefile: 39; sed: 12
file content (170 lines) | stat: -rw-r--r-- 5,929 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
    SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de>
    SPDX-FileCopyrightText: 2020 Igor Kushnir <igorkuo@gmail.com>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#ifndef KDEVPLATFORM_REFERENCECOUNTING_H
#define KDEVPLATFORM_REFERENCECOUNTING_H

#include "serializationexport.h"

#include <QtGlobal>

#include <cstddef>

//When this is enabled, the duchain unloading is disabled as well, and you should start
//with a cleared ~/.kdevduchain
// #define TEST_REFERENCE_COUNTING

namespace KDevelop {
///Since shouldDoDUChainReferenceCounting is called extremely often, we export some internals into the header here,
///so the reference-counting code can be inlined.
class KDEVPLATFORMSERIALIZATION_EXPORT DUChainReferenceCounting
{
    Q_DISABLE_COPY_MOVE(DUChainReferenceCounting)
public:
    using Pointer = const std::byte*;

    bool shouldDo(Pointer item) const noexcept;
    void enable(Pointer start, unsigned size);
    void disable(Pointer start, unsigned size);

    static DUChainReferenceCounting& instance() noexcept
    {
        static thread_local DUChainReferenceCounting duchainReferenceCounting;
        return duchainReferenceCounting;
    }

private:
    // This defaulted default constructor is implicitly noexcept. Marking it as noexcept explicitly, however,
    // doesn't compile with Clang version < 9.0 because of https://bugs.llvm.org/show_bug.cgi?id=33736.
    constexpr DUChainReferenceCounting() = default;

    struct Interval {
        Pointer start;
        unsigned size;
        unsigned refCount;

        constexpr bool contains(Pointer item) const noexcept { return item >= start && item < start + size; }
        void assign(Pointer newStart, unsigned newSize) noexcept;
    };

    Interval* findInterval(Pointer start, unsigned size) noexcept;

    // I have never encountered more than 2 intervals at a time during my tests.
    // So the maximum interval count of 3 should be more than enough for every practical use.
    static constexpr std::size_t maxIntervalCount = 3;

    std::size_t count = 0;
    Interval intervals[maxIntervalCount] = {};
};

inline bool DUChainReferenceCounting::shouldDo(Pointer item) const noexcept
{
    // count == 0 means that no place has been marked for reference counting, occurs in ~99% of cases.
    // Q_UNLIKELY somewhat speeds up BenchIndexedString::bench_qhashIndexedString(),
    // slightly speeds up BenchIndexedString::bench_create() and BenchIndexedString::bench_destroy()
    // but substantially slows down BenchItemRepository::shouldDoReferenceCounting(enabled).
    // However, while the three affected BenchIndexedString benchmarks are more or less realistic,
    // BenchItemRepository::shouldDoReferenceCounting(enabled) is highly synthetic and extremely
    // sensitive to the performance of this function.
    for (std::size_t i = 0; Q_UNLIKELY(i != count); ++i) {
        if (intervals[i].contains(item)) {
            return true;
        }
    }
    return false;
}

KDEVPLATFORMSERIALIZATION_EXPORT void initReferenceCounting();

///This is used by indexed items to decide whether they should do reference-counting
inline bool shouldDoDUChainReferenceCounting(const void* item) noexcept
{
    return DUChainReferenceCounting::instance().shouldDo(reinterpret_cast<DUChainReferenceCounting::Pointer>(item));
}

///Enable reference-counting for the given range
///You should only enable the reference-counting for the time it's really needed,
///and it always has to be enabled too when the items are deleted again, else
///it will lead to inconsistencies in the repository.
///@warning If you are not working on the duchain internal storage mechanism, you should
///not care about this stuff at all.
///@param start Position where to start the reference-counting
///@param size Size of the area in bytes
KDEVPLATFORMSERIALIZATION_EXPORT void enableDUChainReferenceCounting(const void* start, unsigned size);
///Must be called as often as enableDUChainReferenceCounting, with the same ranges
///Must never be called for the same range twice, and not for overlapping ranges
///@param start Position where the reference-counting was started
///@param size Size of the area where the reference-counting was started in bytes
KDEVPLATFORMSERIALIZATION_EXPORT void disableDUChainReferenceCounting(const void* start, unsigned size);

class DUChainReferenceCountingEnabler
{
    Q_DISABLE_COPY_MOVE(DUChainReferenceCountingEnabler)
public:
    explicit DUChainReferenceCountingEnabler(const void* start, unsigned size)
        : m_start{start}
        , m_size{size}
    {
        enableDUChainReferenceCounting(m_start, m_size);
    }

    ~DUChainReferenceCountingEnabler()
    {
        disableDUChainReferenceCounting(m_start, m_size);
    }

private:
    const void* const m_start;
    const unsigned m_size;
};

template <bool markForReferenceCounting>
struct OptionalDUChainReferenceCountingEnabler
{
    explicit OptionalDUChainReferenceCountingEnabler(const void*, unsigned) {}
};

template<>
struct OptionalDUChainReferenceCountingEnabler<true> : DUChainReferenceCountingEnabler
{
    using DUChainReferenceCountingEnabler::DUChainReferenceCountingEnabler;
};

///Use this as local variable within the object that maintains the reference-count,
///and use
struct ReferenceCountManager
{
    #ifndef TEST_REFERENCE_COUNTING
    inline void increase(uint& ref, uint /*targetId*/)
    {
        ++ref;
    }
    inline void decrease(uint& ref, uint /*targetId*/)
    {
        Q_ASSERT(ref);
        --ref;
    }

    #else

    ReferenceCountManager();
    ~ReferenceCountManager();

    ReferenceCountManager(const ReferenceCountManager& rhs);
    ReferenceCountManager& operator=(const ReferenceCountManager& rhs);

    void increase(uint& ref, uint targetId);
    void decrease(uint& ref, uint targetId);

//     bool hasReferenceCount() const;

    uint m_id;
    #endif
};
}

#endif