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