File indexing completed on 2024-05-05 04:38:10

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de>
0003     SPDX-FileCopyrightText: 2020 Igor Kushnir <igorkuo@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KDEVPLATFORM_REFERENCECOUNTING_H
0009 #define KDEVPLATFORM_REFERENCECOUNTING_H
0010 
0011 #include "serializationexport.h"
0012 
0013 #include <QtGlobal>
0014 
0015 #include <cstddef>
0016 
0017 //When this is enabled, the duchain unloading is disabled as well, and you should start
0018 //with a cleared ~/.kdevduchain
0019 // #define TEST_REFERENCE_COUNTING
0020 
0021 namespace KDevelop {
0022 ///Since shouldDoDUChainReferenceCounting is called extremely often, we export some internals into the header here,
0023 ///so the reference-counting code can be inlined.
0024 class KDEVPLATFORMSERIALIZATION_EXPORT DUChainReferenceCounting
0025 {
0026     Q_DISABLE_COPY_MOVE(DUChainReferenceCounting)
0027 public:
0028     using Pointer = const std::byte*;
0029 
0030     bool shouldDo(Pointer item) const noexcept;
0031     void enable(Pointer start, unsigned size);
0032     void disable(Pointer start, unsigned size);
0033 
0034     static DUChainReferenceCounting& instance() noexcept
0035     {
0036         static thread_local DUChainReferenceCounting duchainReferenceCounting;
0037         return duchainReferenceCounting;
0038     }
0039 
0040 private:
0041     // This defaulted default constructor is implicitly noexcept. Marking it as noexcept explicitly, however,
0042     // doesn't compile with Clang version < 9.0 because of https://bugs.llvm.org/show_bug.cgi?id=33736.
0043     constexpr DUChainReferenceCounting() = default;
0044 
0045     struct Interval {
0046         Pointer start;
0047         unsigned size;
0048         unsigned refCount;
0049 
0050         constexpr bool contains(Pointer item) const noexcept { return item >= start && item < start + size; }
0051         void assign(Pointer newStart, unsigned newSize) noexcept;
0052     };
0053 
0054     Interval* findInterval(Pointer start, unsigned size) noexcept;
0055 
0056     // I have never encountered more than 2 intervals at a time during my tests.
0057     // So the maximum interval count of 3 should be more than enough for every practical use.
0058     static constexpr std::size_t maxIntervalCount = 3;
0059 
0060     std::size_t count = 0;
0061     Interval intervals[maxIntervalCount] = {};
0062 };
0063 
0064 inline bool DUChainReferenceCounting::shouldDo(Pointer item) const noexcept
0065 {
0066     // count == 0 means that no place has been marked for reference counting, occurs in ~99% of cases.
0067     // Q_UNLIKELY somewhat speeds up BenchIndexedString::bench_qhashIndexedString(),
0068     // slightly speeds up BenchIndexedString::bench_create() and BenchIndexedString::bench_destroy()
0069     // but substantially slows down BenchItemRepository::shouldDoReferenceCounting(enabled).
0070     // However, while the three affected BenchIndexedString benchmarks are more or less realistic,
0071     // BenchItemRepository::shouldDoReferenceCounting(enabled) is highly synthetic and extremely
0072     // sensitive to the performance of this function.
0073     for (std::size_t i = 0; Q_UNLIKELY(i != count); ++i) {
0074         if (intervals[i].contains(item)) {
0075             return true;
0076         }
0077     }
0078     return false;
0079 }
0080 
0081 KDEVPLATFORMSERIALIZATION_EXPORT void initReferenceCounting();
0082 
0083 ///This is used by indexed items to decide whether they should do reference-counting
0084 inline bool shouldDoDUChainReferenceCounting(const void* item) noexcept
0085 {
0086     return DUChainReferenceCounting::instance().shouldDo(reinterpret_cast<DUChainReferenceCounting::Pointer>(item));
0087 }
0088 
0089 ///Enable reference-counting for the given range
0090 ///You should only enable the reference-counting for the time it's really needed,
0091 ///and it always has to be enabled too when the items are deleted again, else
0092 ///it will lead to inconsistencies in the repository.
0093 ///@warning If you are not working on the duchain internal storage mechanism, you should
0094 ///not care about this stuff at all.
0095 ///@param start Position where to start the reference-counting
0096 ///@param size Size of the area in bytes
0097 KDEVPLATFORMSERIALIZATION_EXPORT void enableDUChainReferenceCounting(const void* start, unsigned size);
0098 ///Must be called as often as enableDUChainReferenceCounting, with the same ranges
0099 ///Must never be called for the same range twice, and not for overlapping ranges
0100 ///@param start Position where the reference-counting was started
0101 ///@param size Size of the area where the reference-counting was started in bytes
0102 KDEVPLATFORMSERIALIZATION_EXPORT void disableDUChainReferenceCounting(const void* start, unsigned size);
0103 
0104 class DUChainReferenceCountingEnabler
0105 {
0106     Q_DISABLE_COPY_MOVE(DUChainReferenceCountingEnabler)
0107 public:
0108     explicit DUChainReferenceCountingEnabler(const void* start, unsigned size)
0109         : m_start{start}
0110         , m_size{size}
0111     {
0112         enableDUChainReferenceCounting(m_start, m_size);
0113     }
0114 
0115     ~DUChainReferenceCountingEnabler()
0116     {
0117         disableDUChainReferenceCounting(m_start, m_size);
0118     }
0119 
0120 private:
0121     const void* const m_start;
0122     const unsigned m_size;
0123 };
0124 
0125 template <bool markForReferenceCounting>
0126 struct OptionalDUChainReferenceCountingEnabler
0127 {
0128     explicit OptionalDUChainReferenceCountingEnabler(const void*, unsigned) {}
0129 };
0130 
0131 template<>
0132 struct OptionalDUChainReferenceCountingEnabler<true> : DUChainReferenceCountingEnabler
0133 {
0134     using DUChainReferenceCountingEnabler::DUChainReferenceCountingEnabler;
0135 };
0136 
0137 ///Use this as local variable within the object that maintains the reference-count,
0138 ///and use
0139 struct ReferenceCountManager
0140 {
0141     #ifndef TEST_REFERENCE_COUNTING
0142     inline void increase(uint& ref, uint /*targetId*/)
0143     {
0144         ++ref;
0145     }
0146     inline void decrease(uint& ref, uint /*targetId*/)
0147     {
0148         Q_ASSERT(ref);
0149         --ref;
0150     }
0151 
0152     #else
0153 
0154     ReferenceCountManager();
0155     ~ReferenceCountManager();
0156 
0157     ReferenceCountManager(const ReferenceCountManager& rhs);
0158     ReferenceCountManager& operator=(const ReferenceCountManager& rhs);
0159 
0160     void increase(uint& ref, uint targetId);
0161     void decrease(uint& ref, uint targetId);
0162 
0163 //     bool hasReferenceCount() const;
0164 
0165     uint m_id;
0166     #endif
0167 };
0168 }
0169 
0170 #endif